1 | // **************************************************************************
|
---|
2 | /** @class StateMachineImp
|
---|
3 |
|
---|
4 | @brief Base class for a state machine implementation
|
---|
5 |
|
---|
6 | \dot
|
---|
7 | digraph example {
|
---|
8 | node [shape=record, fontname=Helvetica, fontsize=10];
|
---|
9 | s [ label="Constructor" style="rounded" color="red" URL="\ref StateMachineImp::StateMachineImp"];
|
---|
10 | a [ label="State -3 (kSM_NotReady)" color="red" URL="\ref StateMachineImp::StateMachineImp"];
|
---|
11 | b [ label="State -2 (kSM_Initializing)" color="red" URL="\ref StateMachineImp::StateMachineImp"];
|
---|
12 | c [ label="State -1 (kSM_Configuring)" color="red" URL="\ref StateMachineImp::StateMachineImp"];
|
---|
13 | y [ label="State 0 (kSM_Ready)" URL="\ref StateMachineImp::Run"];
|
---|
14 | r [ label="User states (Running)" ];
|
---|
15 | e [ label="State 256 (kSM_Error)" ];
|
---|
16 | f [ label="State 65535 (kSM_FatalError)" color="red" URL="\ref StateMachineImp::Run"];
|
---|
17 |
|
---|
18 | // ---- manual means: command or program introduced ----
|
---|
19 |
|
---|
20 | // Startup from Run() to Ready
|
---|
21 | s -> a [ arrowhead="open" color="red" style="solid" ]; // automatic (mandatory)
|
---|
22 | a -> b [ arrowhead="open" color="red" style="solid" ]; // automatic (mandatory)
|
---|
23 | b -> c [ arrowhead="open" color="red" style="solid" ]; // automatic (mandatory)
|
---|
24 |
|
---|
25 | c -> y [ arrowhead="open" color="red" style="solid" URL="\ref StateMachineImp::Run" ]; // prg: Run()
|
---|
26 |
|
---|
27 | y -> c [ arrowhead="open" style="dashed" URL="\ref StateMachineDim::exitHandler" ]; // CMD: EXIT
|
---|
28 | r -> c [ arrowhead="open" style="dashed" URL="\ref StateMachineDim::exitHandler" ]; // CMD: EXIT
|
---|
29 | e -> c [ arrowhead="open" style="dashed" URL="\ref StateMachineDim::exitHandler" ]; // CMD: EXIT
|
---|
30 |
|
---|
31 | e -> y [ arrowhead="open" color="red" style="dashed" ]; // CMD: RESET (e.g.)
|
---|
32 |
|
---|
33 | y -> e [ arrowhead="open" color="blue" style="solid" ]; // prg
|
---|
34 | r -> e [ arrowhead="open" color="blue" style="solid" ]; // prg
|
---|
35 |
|
---|
36 | y -> r [ arrowhead="open" color="blue" style="dashed" ]; // CMD/PRG
|
---|
37 | r -> y [ arrowhead="open" color="blue" style="dashed" ]; // CMD/PRG
|
---|
38 |
|
---|
39 | y -> f [ arrowhead="open" color="blue" style="solid" ]; // prg
|
---|
40 | r -> f [ arrowhead="open" color="blue" style="solid" ]; // prg
|
---|
41 | e -> f [ arrowhead="open" color="blue" style="solid" ]; // prg
|
---|
42 | }
|
---|
43 | \enddot
|
---|
44 |
|
---|
45 | - <B>Red box</B>: Internal states. Events which are received are
|
---|
46 | discarded.
|
---|
47 | - <B>Black box</B>: State machine running. Events are accepted and
|
---|
48 | processed according to the implemented functions Transition(),
|
---|
49 | Configuration() and Execute(). Events are accepted accoding to the
|
---|
50 | lookup table of allowed transitions.
|
---|
51 | - <B>Red solid arrow</B>: A transition initiated by the program itself.
|
---|
52 | - <b>Dashed arrows in general</b>: Transitions which can be initiated
|
---|
53 | by a dim-command or get inistiated by the program.
|
---|
54 | - <b>Solid arrows in general</b>: These transitions are always initiated by
|
---|
55 | the program.
|
---|
56 | - <B>Red dashed</B>: Suggested RESET event (should be implemented by
|
---|
57 | the derived class)
|
---|
58 | - <B>Black dashed arrow</B>: Exit from the main loop. This can either
|
---|
59 | happen by the Dim-provided EXIT-command or a call to StateMachineDim::Stop.
|
---|
60 | - <B>Black arrows</B>: Other events or transitions which can be
|
---|
61 | implemented by the derived class.
|
---|
62 | - <B>Dotted black arrow</B>: Exit from the main-loop which is initiated
|
---|
63 | by the program itself through StateMachineDim::Stop() and not by the
|
---|
64 | state machine itself (Execute(), Configure() and Transition())
|
---|
65 | - <b>Blue dashed arrows</b>: Transitions which happen either by receiving
|
---|
66 | a event or are initiated from the state machine itself
|
---|
67 | (by return values of (Execute(), Configure() and Transition())
|
---|
68 | - <b>Blue solid</b>: Transitions which cannot be initiated by dim
|
---|
69 | event but only by the state machine itself.
|
---|
70 | - From the program point of view the fatal error is identical with
|
---|
71 | the kSM_Configuring state, i.e. it is returned from the main-loop.
|
---|
72 | Usually this will result in program termination. However, depending
|
---|
73 | on the state the program might decide to use different cleaning
|
---|
74 | routines.
|
---|
75 |
|
---|
76 | @todo
|
---|
77 | - A proper and correct cleanup after an EXIT or Stop() is missing.
|
---|
78 | maybe we have to force a state 0 first?
|
---|
79 | */
|
---|
80 | // **************************************************************************
|
---|
81 | #include "StateMachineImp.h"
|
---|
82 |
|
---|
83 | #include "Time.h"
|
---|
84 | #include "Event.h"
|
---|
85 |
|
---|
86 | #include "WindowLog.h"
|
---|
87 | #include "Converter.h"
|
---|
88 |
|
---|
89 | #include "tools.h"
|
---|
90 |
|
---|
91 | using namespace std;
|
---|
92 |
|
---|
93 | // --------------------------------------------------------------------------
|
---|
94 | //
|
---|
95 | //! The state of the state machine (fCurrentState) is initialized with
|
---|
96 | //! kSM_NotReady
|
---|
97 | //!
|
---|
98 | //! Default state names for kSM_NotReady, kSM_Ready, kSM_Error and
|
---|
99 | //! kSM_FatalError are set via AddStateName.
|
---|
100 | //!
|
---|
101 | //! fExitRequested is set to 0, fRunning to false.
|
---|
102 | //!
|
---|
103 | //! Furthermore, the ostream is propagated to MessageImp, as well as
|
---|
104 | //! stored in fOut.
|
---|
105 | //!
|
---|
106 | //! MessageImp is used for messages which are distributed (e.g. via DIM),
|
---|
107 | //! fOut is used for messages which are only displayed on the local console.
|
---|
108 | //!
|
---|
109 | //! Subsequent, i.e. derived classes should setup all allowed state
|
---|
110 | //! transitions as well as all allowed configuration event by
|
---|
111 | //! AddEvent and AddStateName.
|
---|
112 | //!
|
---|
113 | //! @param out
|
---|
114 | //! A refrence to an ostream which allows to redirect the log-output
|
---|
115 | //! to something else than cout. The default is cout. The reference
|
---|
116 | //! is propagated to fLog
|
---|
117 | //!
|
---|
118 | //! @param name
|
---|
119 | //! The server name stored in fName
|
---|
120 | //!
|
---|
121 | //
|
---|
122 | StateMachineImp::StateMachineImp(ostream &out, const std::string &name)
|
---|
123 | : MessageImp(out), fName(name), fCurrentState(kSM_NotReady),
|
---|
124 | fRunning(false), fExitRequested(0)
|
---|
125 | {
|
---|
126 | SetDefaultStateNames();
|
---|
127 | }
|
---|
128 |
|
---|
129 | // --------------------------------------------------------------------------
|
---|
130 | //
|
---|
131 | //! delete all object stored in fListOfEvent and in fEventQueue
|
---|
132 | //
|
---|
133 | StateMachineImp::~StateMachineImp()
|
---|
134 | {
|
---|
135 | // For this to work EventImp must be the first class from which
|
---|
136 | // the object inherits
|
---|
137 | for (vector<EventImp*>::iterator cmd=fListOfEvents.begin(); cmd!=fListOfEvents.end(); cmd++)
|
---|
138 | delete *cmd;
|
---|
139 |
|
---|
140 | // Unfortunately, front() doesn't necessarily return 0 if
|
---|
141 | // queue is empty
|
---|
142 | if (fEventQueue.size())
|
---|
143 | {
|
---|
144 | while (1)
|
---|
145 | {
|
---|
146 | Event *q=fEventQueue.front();
|
---|
147 | if (!q)
|
---|
148 | break;
|
---|
149 |
|
---|
150 | fEventQueue.pop();
|
---|
151 | delete q;
|
---|
152 | }
|
---|
153 | }
|
---|
154 | }
|
---|
155 |
|
---|
156 | // --------------------------------------------------------------------------
|
---|
157 | //
|
---|
158 | //! Sets the default state names. This function should be called in
|
---|
159 | //! derived classes again if they overwrite SetStateName().
|
---|
160 | //
|
---|
161 | void StateMachineImp::SetDefaultStateNames()
|
---|
162 | {
|
---|
163 | AddStateName(kSM_NotReady, "NotReady", "State machine not ready, events are ignored.");
|
---|
164 | AddStateName(kSM_Ready, "Ready", "State machine ready to receive events.");
|
---|
165 | AddStateName(kSM_Error, "ERROR", "Common error state.");
|
---|
166 | AddStateName(kSM_FatalError, "FATAL", "A fatal error occured, the eventloop is stopped.");
|
---|
167 | }
|
---|
168 |
|
---|
169 | // --------------------------------------------------------------------------
|
---|
170 | //
|
---|
171 | //! Puts the given event into the fifo. The fifo will take over ownership.
|
---|
172 | //! Access to fEventQueue is encapsulated by fMutex.
|
---|
173 | //!
|
---|
174 | //! @param cmd
|
---|
175 | //! Pointer to an object of type Event to be stored in the fifo
|
---|
176 | //!
|
---|
177 | //! @todo
|
---|
178 | //! Can we also allow EventImp?
|
---|
179 | //
|
---|
180 | void StateMachineImp::PushEvent(Event *cmd)
|
---|
181 | {
|
---|
182 | fMutex.lock();
|
---|
183 | fEventQueue.push(cmd);
|
---|
184 | fMutex.unlock();
|
---|
185 | }
|
---|
186 |
|
---|
187 | // --------------------------------------------------------------------------
|
---|
188 | //
|
---|
189 | //! Get an event from the fifo. We will take over the owenership of the
|
---|
190 | //! object. The pointer is deleted from the fifo. Access of fEventQueue
|
---|
191 | //! is encapsulated by fMutex.
|
---|
192 | //!
|
---|
193 | //! @returns
|
---|
194 | //! A pointer to an Event object
|
---|
195 | //
|
---|
196 | Event *StateMachineImp::PopEvent()
|
---|
197 | {
|
---|
198 | fMutex.lock();
|
---|
199 |
|
---|
200 | // Get the next event from the stack
|
---|
201 | // and remove event from the stack
|
---|
202 | Event *cmd = fEventQueue.front();
|
---|
203 | fEventQueue.pop();
|
---|
204 |
|
---|
205 | fMutex.unlock();
|
---|
206 |
|
---|
207 | return cmd;
|
---|
208 | }
|
---|
209 |
|
---|
210 | // --------------------------------------------------------------------------
|
---|
211 | //
|
---|
212 | //! With this function commands are posted to the event queue. The data
|
---|
213 | //! is not given as binary data but as a string instead. It is converted
|
---|
214 | //! according to the format of the corresponding event and an event
|
---|
215 | //! is posted to the queue if successfull.
|
---|
216 | //!
|
---|
217 | //! @param lout
|
---|
218 | //! Stream to which output should be redirected
|
---|
219 | //! event should be for.
|
---|
220 | //!
|
---|
221 | //! @param str
|
---|
222 | //! Command with data, e.g. "COMMAND 1 2 3 4 5 test"
|
---|
223 | //!
|
---|
224 | //! @returns
|
---|
225 | //! false if no event was posted to the queue. If
|
---|
226 | //! PostEvent(EventImp&,const char*, size_t) was called return its
|
---|
227 | //! return value
|
---|
228 | //
|
---|
229 | bool StateMachineImp::PostEvent(ostream &lout, const string &str)
|
---|
230 | {
|
---|
231 | // Find the delimiter between the command name and the data
|
---|
232 | size_t p0 = str.find_first_of(' ');
|
---|
233 | if (p0==string::npos)
|
---|
234 | p0 = str.length();
|
---|
235 |
|
---|
236 | // Compile the command which will be sent to the state-machine
|
---|
237 | const string name = fName + "/" + str.substr(0, p0);
|
---|
238 |
|
---|
239 | // Check if this command is existing at all
|
---|
240 | EventImp *evt = FindEvent(name);
|
---|
241 | if (!evt)
|
---|
242 | {
|
---|
243 | lout << kRed << "Unknown command '" << name << "'" << endl;
|
---|
244 | return false;
|
---|
245 | }
|
---|
246 |
|
---|
247 | // Get the format of the event data
|
---|
248 | const string fmt = evt->GetFormat();
|
---|
249 |
|
---|
250 | // Convert the user entered data according to the format string
|
---|
251 | // into a data block which will be attached to the event
|
---|
252 | #ifndef DEBUG
|
---|
253 | ostringstream sout;
|
---|
254 | const Converter conv(sout, fmt, false);
|
---|
255 | #else
|
---|
256 | const Converter conv(lout, fmt, false);
|
---|
257 | #endif
|
---|
258 | if (!conv)
|
---|
259 | {
|
---|
260 | lout << kRed << "Couldn't properly parse the format... ignored." << endl;
|
---|
261 | return false;
|
---|
262 | }
|
---|
263 |
|
---|
264 | try
|
---|
265 | {
|
---|
266 | #ifdef DEBUG
|
---|
267 | lout << kBlue << name;
|
---|
268 | #endif
|
---|
269 | const vector<char> v = conv.GetVector(str.substr(p0));
|
---|
270 | #ifdef DEBUG
|
---|
271 | lout << endl;
|
---|
272 | #endif
|
---|
273 |
|
---|
274 | return PostEvent(*evt, v.data(), v.size());
|
---|
275 | }
|
---|
276 | catch (const std::runtime_error &e)
|
---|
277 | {
|
---|
278 | lout << endl << kRed << e.what() << endl;
|
---|
279 | }
|
---|
280 |
|
---|
281 | return false;
|
---|
282 | }
|
---|
283 |
|
---|
284 | // --------------------------------------------------------------------------
|
---|
285 | //
|
---|
286 | //! With this function commands are posted to the event queue. If the
|
---|
287 | //! event loop has not yet been started with Run() the command is directly
|
---|
288 | //! handled by HandleEvent.
|
---|
289 | //!
|
---|
290 | //! Events posted when the state machine is in a negative state or
|
---|
291 | //! kSM_FatalError are ignored.
|
---|
292 | //!
|
---|
293 | //! A new event is created and its data contents initialized with the
|
---|
294 | //! specified memory.
|
---|
295 | //!
|
---|
296 | //! @param evt
|
---|
297 | //! The event to be posted. The precise contents depend on what the
|
---|
298 | //! event should be for.
|
---|
299 | //!
|
---|
300 | //! @param ptr
|
---|
301 | //! pointer to the memory which should be attached to the event
|
---|
302 | //!
|
---|
303 | //! @param siz
|
---|
304 | //! size of the memory which should be attached to the event
|
---|
305 | //!
|
---|
306 | //! @returns
|
---|
307 | //! false if the event is ignored, true otherwise.
|
---|
308 | //!
|
---|
309 | //! @todo
|
---|
310 | //! - Shell we check for the validity of a command at the current state, too?
|
---|
311 | //! - should we also get the output stream as an argument here?
|
---|
312 | //
|
---|
313 | bool StateMachineImp::PostEvent(const EventImp &evt, const char *ptr, size_t siz)
|
---|
314 | {
|
---|
315 | if (GetCurrentState()<0 || GetCurrentState()==kSM_FatalError)
|
---|
316 | {
|
---|
317 | Out() << kYellow << "State<0 or FatalError: Event ignored." << endl;
|
---|
318 | return false;
|
---|
319 | }
|
---|
320 |
|
---|
321 | if (IsRunning())
|
---|
322 | {
|
---|
323 | Event *event = new Event(evt, ptr, siz);
|
---|
324 | //Debug("Posted: "+event->GetName());
|
---|
325 | PushEvent(event);
|
---|
326 | }
|
---|
327 | else
|
---|
328 | {
|
---|
329 | // FIXME: Is this thread safe? (Yes, because the data is copied)
|
---|
330 | // But two handlers could be called at the same time. Do we
|
---|
331 | // need to lock the handlers? (Dim + console)
|
---|
332 | // FIXME: Is copying of the data necessary?
|
---|
333 | const Event event(evt, ptr, siz);
|
---|
334 | Lock();
|
---|
335 | HandleEvent(event);
|
---|
336 | UnLock();
|
---|
337 | }
|
---|
338 | return true;
|
---|
339 | }
|
---|
340 |
|
---|
341 | // --------------------------------------------------------------------------
|
---|
342 | //
|
---|
343 | //! With this function commands are posted to the event queue. If the
|
---|
344 | //! event loop has not yet been started with Run() the command is directly
|
---|
345 | //! handled by HandleEvent.
|
---|
346 | //!
|
---|
347 | //! Events posted when the state machine is in a negative state or
|
---|
348 | //! kSM_FatalError are ignored.
|
---|
349 | //!
|
---|
350 | //! @param evt
|
---|
351 | //! The event to be posted. The precise contents depend on what the
|
---|
352 | //! event should be for.
|
---|
353 | //!
|
---|
354 | //! @returns
|
---|
355 | //! false if the event is ignored, true otherwise.
|
---|
356 | //!
|
---|
357 | //! @todo
|
---|
358 | //! - Shell we check for the validity of a command at the current state, too?
|
---|
359 | //! - should we also get the output stream as an argument here?
|
---|
360 | //
|
---|
361 | bool StateMachineImp::PostEvent(const EventImp &evt)
|
---|
362 | {
|
---|
363 | if (GetCurrentState()<0 || GetCurrentState()==kSM_FatalError)
|
---|
364 | {
|
---|
365 | Out() << kYellow << "State<0 or FatalError: Event ignored." << endl;
|
---|
366 | return false;
|
---|
367 | }
|
---|
368 |
|
---|
369 | if (IsRunning())
|
---|
370 | PushEvent(new Event(evt));
|
---|
371 | else
|
---|
372 | {
|
---|
373 | // FIXME: Is this thread safe? (Yes, because it is only used
|
---|
374 | // by Dim and this is thread safe) But two handlers could
|
---|
375 | // be called at the same time. Do we need to lock the handlers?
|
---|
376 | HandleEvent(evt);
|
---|
377 | }
|
---|
378 | return true;
|
---|
379 | }
|
---|
380 |
|
---|
381 | // --------------------------------------------------------------------------
|
---|
382 | //
|
---|
383 | //! Return all event names of the StateMachine
|
---|
384 | //!
|
---|
385 | //! @returns
|
---|
386 | //! A vector of strings with all event names of the state machine.
|
---|
387 | //! The event names all have the SERVER/ pre-fix removed.
|
---|
388 | //
|
---|
389 | const vector<string> StateMachineImp::GetEventNames() const
|
---|
390 | {
|
---|
391 | vector<string> v;
|
---|
392 |
|
---|
393 | const string &name = fName + "/";
|
---|
394 | const int len = name.length();
|
---|
395 |
|
---|
396 | for (vector<EventImp*>::const_iterator i=fListOfEvents.begin();
|
---|
397 | i!=fListOfEvents.end(); i++)
|
---|
398 | {
|
---|
399 | const string evt = (*i)->GetName();
|
---|
400 |
|
---|
401 | v.push_back(evt.substr(0, len)==name ? evt.substr(len) : evt);
|
---|
402 | }
|
---|
403 |
|
---|
404 | return v;
|
---|
405 | }
|
---|
406 |
|
---|
407 | // --------------------------------------------------------------------------
|
---|
408 | //
|
---|
409 | //! Call for each event in fListEvents its Print function with the given
|
---|
410 | //! stream.
|
---|
411 | //!
|
---|
412 | //! @param out
|
---|
413 | //! ostream to which the output should be redirected
|
---|
414 | //!
|
---|
415 | //! @param evt
|
---|
416 | //! if given only the given event is selected
|
---|
417 | //
|
---|
418 | void StateMachineImp::PrintListOfEvents(ostream &out, const string &evt) const
|
---|
419 | {
|
---|
420 | for (vector<EventImp*>::const_iterator c=fListOfEvents.begin(); c!=fListOfEvents.end(); c++)
|
---|
421 | if (evt.empty() || GetName()+'/'+evt==(*c)->GetName())
|
---|
422 | (*c)->Print(out, true);
|
---|
423 | }
|
---|
424 |
|
---|
425 | // --------------------------------------------------------------------------
|
---|
426 | //
|
---|
427 | //! Call for each event in fListEvents its Print function with the given
|
---|
428 | //! stream if it is an allowed event in the current state.
|
---|
429 | //!
|
---|
430 | //! @param out
|
---|
431 | //! ostream to which the output should be redirected
|
---|
432 | //!
|
---|
433 | //
|
---|
434 | void StateMachineImp::PrintListOfAllowedEvents(ostream &out) const
|
---|
435 | {
|
---|
436 | for (vector<EventImp*>::const_iterator c=fListOfEvents.begin(); c!=fListOfEvents.end(); c++)
|
---|
437 | if ((*c)->IsStateAllowed(fCurrentState))
|
---|
438 | (*c)->Print(out, true);
|
---|
439 | }
|
---|
440 |
|
---|
441 | // --------------------------------------------------------------------------
|
---|
442 | //
|
---|
443 | //! Call PrintListOfEvents with fOut as the output stream
|
---|
444 | //!
|
---|
445 | //! @param str
|
---|
446 | //! if given only the given event is selected
|
---|
447 | //
|
---|
448 | //
|
---|
449 | void StateMachineImp::PrintListOfEvents(const string &str) const
|
---|
450 | {
|
---|
451 | PrintListOfEvents(Out(), str);
|
---|
452 | }
|
---|
453 |
|
---|
454 | // --------------------------------------------------------------------------
|
---|
455 | //
|
---|
456 | //! Print a list of all states with descriptions.
|
---|
457 | //!
|
---|
458 | //! @param out
|
---|
459 | //! ostream to which the output should be redirected
|
---|
460 | //
|
---|
461 | void StateMachineImp::PrintListOfStates(std::ostream &out) const
|
---|
462 | {
|
---|
463 | out << endl;
|
---|
464 | out << kBold << "List of available states:" << endl;
|
---|
465 | for (StateNames::const_iterator i=fStateNames.begin(); i!=fStateNames.end(); i++)
|
---|
466 | {
|
---|
467 | ostringstream state;
|
---|
468 | state << i->first;
|
---|
469 | out << " -[" << kBold << state.str() << kReset << "]:" << setfill(' ') << setw(6-state.str().length()) << ' ' << kYellow << i->second.first << kBlue << " (" << i->second.second << ")" << endl;
|
---|
470 | }
|
---|
471 | out << endl;
|
---|
472 | }
|
---|
473 |
|
---|
474 | // --------------------------------------------------------------------------
|
---|
475 | //
|
---|
476 | //! Print a list of all states with descriptions.
|
---|
477 | //
|
---|
478 | void StateMachineImp::PrintListOfStates() const
|
---|
479 | {
|
---|
480 | PrintListOfStates(Out());
|
---|
481 | }
|
---|
482 |
|
---|
483 | // --------------------------------------------------------------------------
|
---|
484 | //
|
---|
485 | //! Check whether an event (same pointer!) is in fListOfEvents
|
---|
486 | //!
|
---|
487 | //! @returns
|
---|
488 | //! true if the event was found, false otherwise
|
---|
489 | //
|
---|
490 | bool StateMachineImp::HasEvent(const EventImp *cmd) const
|
---|
491 | {
|
---|
492 | // Find the event from the list of commands and queue it
|
---|
493 | return find(fListOfEvents.begin(), fListOfEvents.end(), cmd)!=fListOfEvents.end();
|
---|
494 | }
|
---|
495 |
|
---|
496 | // --------------------------------------------------------------------------
|
---|
497 | //
|
---|
498 | //! Check whether an event with the given name is found in fListOfEvents.
|
---|
499 | //! Note that currently there is no mechanism which ensures that not two
|
---|
500 | //! events have the same name.
|
---|
501 | //!
|
---|
502 | //! @returns
|
---|
503 | //! true if the event was found, false otherwise
|
---|
504 | //
|
---|
505 | EventImp *StateMachineImp::FindEvent(const std::string &evt) const
|
---|
506 | {
|
---|
507 | // Find the command from the list of commands and queue it
|
---|
508 | for (vector<EventImp*>::const_iterator c=fListOfEvents.begin(); c!=fListOfEvents.end(); c++)
|
---|
509 | if (evt == (*c)->GetName())
|
---|
510 | return *c;
|
---|
511 |
|
---|
512 | return 0;
|
---|
513 | }
|
---|
514 |
|
---|
515 | // --------------------------------------------------------------------------
|
---|
516 | //
|
---|
517 | //! Returns a pointer to a newly allocated object of base EventImp.
|
---|
518 | //! It is meant to be overloaded by derived classes to create their
|
---|
519 | //! own kind of events.
|
---|
520 | //!
|
---|
521 | //! @param targetstate
|
---|
522 | //! Defines the target state of the new transition. If \b must be
|
---|
523 | //! greater or equal zero. A negative target state is used to flag
|
---|
524 | //! commands which do not initiate a state transition. If this is
|
---|
525 | //! desired use AddEvent instead.
|
---|
526 | //!
|
---|
527 | //! @param name
|
---|
528 | //! The command name which should initiate the transition. The DimCommand
|
---|
529 | //! will be constructed with the name given to the constructor and this
|
---|
530 | //! name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE"
|
---|
531 | //!
|
---|
532 | //! @param fmt
|
---|
533 | //! A format as defined by the dim system can be given for the command.
|
---|
534 | //! However, it has no real meaning except that it is stored within the
|
---|
535 | //! DimCommand object. However, the user must make sure that the data of
|
---|
536 | //! received commands is properly extracted. No check is done.
|
---|
537 | //
|
---|
538 | EventImp *StateMachineImp::CreateEvent(int targetstate, const char *, const char *)
|
---|
539 | {
|
---|
540 | return new EventImp(targetstate);
|
---|
541 | }
|
---|
542 |
|
---|
543 | // --------------------------------------------------------------------------
|
---|
544 | //
|
---|
545 | //! Calling this function, a new (named) event is added to the state
|
---|
546 | //! machine. Via a call to CreateEvent a new event is created with the
|
---|
547 | //! given targetstate, name and format.
|
---|
548 | //!
|
---|
549 | //! The allowed states are passed to the new event and a message
|
---|
550 | //! is written to the output-stream.
|
---|
551 | //!
|
---|
552 | //! @param targetstate
|
---|
553 | //! Defines the target state (or name) of the new event. If \b must be
|
---|
554 | //! greater or equal zero. A negative target state is used to flag
|
---|
555 | //! commands which do not initiate a state transition. If this is
|
---|
556 | //! desired use the unnamed version of AddEvent instead.
|
---|
557 | //!
|
---|
558 | //! @param name
|
---|
559 | //! The command name which should initiate the transition. The DimCommand
|
---|
560 | //! will be constructed with the name given to the constructor and this
|
---|
561 | //! name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE"
|
---|
562 | //!
|
---|
563 | //! @param states
|
---|
564 | //! A comma sepeareted list of ints, e.g. "1, 4, 5, 9" with states
|
---|
565 | //! in which this new state transition is allowed and will be accepted.
|
---|
566 | //!
|
---|
567 | //! @param fmt
|
---|
568 | //! A format as defined by the dim system can be given for the command.
|
---|
569 | //! However, it has no real meaning except that it is stored within the
|
---|
570 | //! DimCommand object. However, the user must make sure that the data of
|
---|
571 | //! received commands is properly extracted. No check is done.
|
---|
572 | //
|
---|
573 | EventImp &StateMachineImp::AddEvent(int targetstate, const char *name, const char *states, const char *fmt)
|
---|
574 | {
|
---|
575 | EventImp *evt = CreateEvent(targetstate, name, fmt);
|
---|
576 |
|
---|
577 | evt->AddAllowedStates(states);
|
---|
578 |
|
---|
579 | #ifdef DEBUG
|
---|
580 | Out() << ": " << Time().GetAsStr("%H:%M:%S.%f");
|
---|
581 | Out() << " - Adding command " << evt->GetName();
|
---|
582 | if (evt->GetTargetState()>=0)
|
---|
583 | Out() << " (transition to " << GetStateDescription(evt->GetTargetState()) << ")";
|
---|
584 | Out() << endl;
|
---|
585 | #endif
|
---|
586 |
|
---|
587 | fListOfEvents.push_back(evt);
|
---|
588 |
|
---|
589 | return *evt;
|
---|
590 | }
|
---|
591 |
|
---|
592 | // --------------------------------------------------------------------------
|
---|
593 | //
|
---|
594 | //! Calling this function, a new (named) event is added to the state
|
---|
595 | //! machine. Therefore an instance of type DimEvent is created and added
|
---|
596 | //! to the list of available commands fListOfEvents.
|
---|
597 | //!
|
---|
598 | //! @param targetstate
|
---|
599 | //! Defines the target state (or name) of the new event. If \b must be
|
---|
600 | //! greater or equal zero. A negative target state is used to flag
|
---|
601 | //! commands which do not initiate a state transition. If this is
|
---|
602 | //! desired use the unnamed version of AddEvent instead.
|
---|
603 | //!
|
---|
604 | //! @param name
|
---|
605 | //! The command name which should initiate the transition. The DimCommand
|
---|
606 | //! will be constructed with the name given to the constructor and this
|
---|
607 | //! name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE"
|
---|
608 | //!
|
---|
609 | //! @param s1, s2, s3, s4, s5
|
---|
610 | //! A list of states from which a transition to targetstate is allowed
|
---|
611 | //! by this command.
|
---|
612 | //
|
---|
613 | EventImp &StateMachineImp::AddEvent(int targetstate, const char *name, int s1, int s2, int s3, int s4, int s5)
|
---|
614 | {
|
---|
615 | ostringstream str;
|
---|
616 | str << s1 << ' ' << s2 << ' ' << s3 << ' ' << s4 << ' ' << s5;
|
---|
617 | return AddEvent(targetstate, name, str.str().c_str(), "");
|
---|
618 | }
|
---|
619 |
|
---|
620 | // --------------------------------------------------------------------------
|
---|
621 | //
|
---|
622 | //! Calling this function, a new (named) event is added to the state
|
---|
623 | //! machine. Therefore an instance of type DimEvent is created and added
|
---|
624 | //! to the list of available commands fListOfEvents.
|
---|
625 | //!
|
---|
626 | //! @param targetstate
|
---|
627 | //! Defines the target state (or name) of the new event. If \b must be
|
---|
628 | //! greater or equal zero. A negative target state is used to flag
|
---|
629 | //! commands which do not initiate a state transition. If this is
|
---|
630 | //! desired use the unnamed version of AddEvent instead.
|
---|
631 | //!
|
---|
632 | //! @param name
|
---|
633 | //! The command name which should initiate the transition. The DimCommand
|
---|
634 | //! will be constructed with the name given to the constructor and this
|
---|
635 | //! name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE"
|
---|
636 | //!
|
---|
637 | //! @param fmt
|
---|
638 | //! A format as defined by the dim system can be given for the command.
|
---|
639 | //! However, it has no real meaning except that it is stored within the
|
---|
640 | //! DimCommand object. However, the user must make sure that the data of
|
---|
641 | //! received commands is properly extracted. No check is done.
|
---|
642 | //!
|
---|
643 | //! @param s1, s2, s3, s4, s5
|
---|
644 | //! A list of states from which a transition to targetstate is allowed
|
---|
645 | //! by this command.
|
---|
646 | //
|
---|
647 | EventImp &StateMachineImp::AddEvent(int targetstate, const char *name, const char *fmt, int s1, int s2, int s3, int s4, int s5)
|
---|
648 | {
|
---|
649 | ostringstream str;
|
---|
650 | str << s1 << ' ' << s2 << ' ' << s3 << ' ' << s4 << ' ' << s5;
|
---|
651 | return AddEvent(targetstate, name, str.str().c_str(), fmt);
|
---|
652 | }
|
---|
653 |
|
---|
654 | // --------------------------------------------------------------------------
|
---|
655 | //
|
---|
656 | //! This function calls AddEvent with a target-state of -1 (unnamed
|
---|
657 | //! event). This shell be used for configuration commands. As well as
|
---|
658 | //! in AddEvent the states in which such a configuration command is
|
---|
659 | //! accepted can be given.
|
---|
660 | //!
|
---|
661 | //! @param name
|
---|
662 | //! The command name which should initiate the transition. The DimCommand
|
---|
663 | //! will be constructed with the name given to the constructor and this
|
---|
664 | //! name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE"
|
---|
665 | //!
|
---|
666 | //! @param states
|
---|
667 | //! A comma sepeareted list of ints, e.g. "1, 4, 5, 9" with states
|
---|
668 | //! in which this new state transition is allowed and will be accepted.
|
---|
669 | //!
|
---|
670 | //! @param fmt
|
---|
671 | //! A format as defined by the dim system can be given for the command.
|
---|
672 | //! However, it has no real meaning except that it is stored within the
|
---|
673 | //! DimCommand object. However, the user must make sure that the data of
|
---|
674 | //! received commands is properly extracted. No check is done.
|
---|
675 | //!
|
---|
676 | EventImp &StateMachineImp::AddEvent(const char *name, const char *states, const char *fmt)
|
---|
677 | {
|
---|
678 | return AddEvent(-1, name, states, fmt);
|
---|
679 | }
|
---|
680 |
|
---|
681 | // --------------------------------------------------------------------------
|
---|
682 | //
|
---|
683 | //! This function calls AddEvent with a target-state of -1 (unnamed
|
---|
684 | //! event). This shell be used for configuration commands. As well as
|
---|
685 | //! in AddEvent the states in which such a configuration command is
|
---|
686 | //! accepted can be given.
|
---|
687 | //!
|
---|
688 | //! @param name
|
---|
689 | //! The command name which should initiate the transition. The DimCommand
|
---|
690 | //! will be constructed with the name given to the constructor and this
|
---|
691 | //! name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE"
|
---|
692 | //!
|
---|
693 | //! @param s1, s2, s3, s4, s5
|
---|
694 | //! A list of states from which a transition to targetstate is allowed
|
---|
695 | //! by this command.
|
---|
696 | //
|
---|
697 | EventImp &StateMachineImp::AddEvent(const char *name, int s1, int s2, int s3, int s4, int s5)
|
---|
698 | {
|
---|
699 | return AddEvent(-1, name, s1, s2, s3, s4, s5);
|
---|
700 | }
|
---|
701 |
|
---|
702 | // --------------------------------------------------------------------------
|
---|
703 | //
|
---|
704 | //! This function calls AddEvent with a target-state of -1 (unnamed
|
---|
705 | //! event). This shell be used for configuration commands. As well as
|
---|
706 | //! in AddEvent the states in which such a configuration command is
|
---|
707 | //! accepted can be given.
|
---|
708 | //!
|
---|
709 | //! @param name
|
---|
710 | //! The command name which should initiate the transition. The DimCommand
|
---|
711 | //! will be constructed with the name given to the constructor and this
|
---|
712 | //! name, e.g. "DRIVE/CHANGE_STATE_TO_NEW_STATE"
|
---|
713 | //!
|
---|
714 | //! @param fmt
|
---|
715 | //! A format as defined by the dim system can be given for the command.
|
---|
716 | //! However, it has no real meaning except that it is stored within the
|
---|
717 | //! DimCommand object. However, the user must make sure that the data of
|
---|
718 | //! received commands is properly extracted. No check is done.
|
---|
719 | //!
|
---|
720 | //! @param s1, s2, s3, s4, s5
|
---|
721 | //! A list of states from which a transition to targetstate is allowed
|
---|
722 | //! by this command.
|
---|
723 | //
|
---|
724 | EventImp &StateMachineImp::AddEvent(const char *name, const char *fmt, int s1, int s2, int s3, int s4, int s5)
|
---|
725 | {
|
---|
726 | return AddEvent(-1, name, fmt, s1, s2, s3, s4, s5);
|
---|
727 | }
|
---|
728 |
|
---|
729 | // --------------------------------------------------------------------------
|
---|
730 | //
|
---|
731 | //! To be able to name states, i.e. present the current state in human
|
---|
732 | //! readable for to the user, a string can be assigned to each state.
|
---|
733 | //! For each state this function can be called only once, i.e. state name
|
---|
734 | //! cannot be overwritten.
|
---|
735 | //!
|
---|
736 | //! Be aware that two states should not have the same name!
|
---|
737 | //!
|
---|
738 | //! @param state
|
---|
739 | //! Number of the state to which a name should be assigned
|
---|
740 | //!
|
---|
741 | //! @param name
|
---|
742 | //! A name which should be assigned to the state, e.g. "Tracking"
|
---|
743 | //!
|
---|
744 | //! @param doc
|
---|
745 | //! A explanatory text describing the state
|
---|
746 | //!
|
---|
747 | void StateMachineImp::AddStateName(const int state, const std::string &name, const std::string &doc)
|
---|
748 | {
|
---|
749 | if (fStateNames[state].first.empty())
|
---|
750 | fStateNames[state] = make_pair(name, doc);
|
---|
751 | }
|
---|
752 |
|
---|
753 | // --------------------------------------------------------------------------
|
---|
754 | //
|
---|
755 | //! @param state
|
---|
756 | //! The state for which the name should be returned.
|
---|
757 | //!
|
---|
758 | //! @returns
|
---|
759 | //! The state name as stored in fStateNames is returned, corresponding
|
---|
760 | //! to the state given. If no name exists the number is returned
|
---|
761 | //! as string.
|
---|
762 | //!
|
---|
763 | const string StateMachineImp::GetStateName(int state) const
|
---|
764 | {
|
---|
765 | const StateNames::const_iterator i = fStateNames.find(state);
|
---|
766 |
|
---|
767 | ostringstream s;
|
---|
768 | s << state;
|
---|
769 | return i==fStateNames.end() || i->second.first.empty() ? s.str() : i->second.first;
|
---|
770 | }
|
---|
771 |
|
---|
772 | // --------------------------------------------------------------------------
|
---|
773 | //
|
---|
774 | //! @param state
|
---|
775 | //! The state for which the name should be returned.
|
---|
776 | //!
|
---|
777 | //! @returns
|
---|
778 | //! The description of a state name as stored in fStateNames is returned,
|
---|
779 | //! corresponding to the state given. If no name exists an empty string is
|
---|
780 | //! returned.
|
---|
781 | //!
|
---|
782 | const string StateMachineImp::GetStateDesc(int state) const
|
---|
783 | {
|
---|
784 | const StateNames::const_iterator i = fStateNames.find(state);
|
---|
785 | return i==fStateNames.end() ? "" : i->second.second;
|
---|
786 | }
|
---|
787 |
|
---|
788 | // --------------------------------------------------------------------------
|
---|
789 | //
|
---|
790 | //! This functions works in analogy to GetStateName, but the state number
|
---|
791 | //! is added in []-parenthesis after the state name if it is available.
|
---|
792 | //!
|
---|
793 | //! @param state
|
---|
794 | //! The state for which the name should be returned.
|
---|
795 | //!
|
---|
796 | //! @returns
|
---|
797 | //! The state name as stored in fStateName is returned corresponding
|
---|
798 | //! to the state given plus the state number added in []-parenthesis.
|
---|
799 | //! If no name exists the number is returned as string.
|
---|
800 | //!
|
---|
801 | //
|
---|
802 | const string StateMachineImp::GetStateDescription(int state) const
|
---|
803 | {
|
---|
804 | const string &str = GetStateName(state);
|
---|
805 |
|
---|
806 | ostringstream s;
|
---|
807 | s << state;
|
---|
808 | if (str==s.str())
|
---|
809 | return str;
|
---|
810 |
|
---|
811 | return str.empty() ? s.str() : (str+'['+s.str()+']');
|
---|
812 | }
|
---|
813 |
|
---|
814 | // --------------------------------------------------------------------------
|
---|
815 | //
|
---|
816 | //! This function is a helpter function to do all the corresponding action
|
---|
817 | //! if the state machine decides to change its state.
|
---|
818 | //!
|
---|
819 | //! If state is equal to the current state (fCurrentState) nothing is done.
|
---|
820 | //! Then the service STATE (fSrcState) is updated with the new state
|
---|
821 | //! and the text message and updateService() is called to distribute
|
---|
822 | //! the update to all clients.
|
---|
823 | //!
|
---|
824 | //! In addition a log message is created and set via UpdateMsg.
|
---|
825 | //!
|
---|
826 | //! @param state
|
---|
827 | //! The new state which should be applied
|
---|
828 | //!
|
---|
829 | //! @param txt
|
---|
830 | //! A text corresponding to the state change which is distributed
|
---|
831 | //! together with the state itself for convinience.
|
---|
832 | //!
|
---|
833 | //! @param cmd
|
---|
834 | //! This argument can be used to give an additional name of the function
|
---|
835 | //! which is reponsible for the state change. It will be included in the
|
---|
836 | //! message
|
---|
837 | //!
|
---|
838 | //! @return
|
---|
839 | //! return the new state which was set or -1 in case of no change
|
---|
840 | //
|
---|
841 | string StateMachineImp::SetCurrentState(int state, const char *txt, const std::string &cmd)
|
---|
842 | {
|
---|
843 | if (state==fCurrentState)
|
---|
844 | {
|
---|
845 | Out() << " -- " << Time().GetAsStr() << ": State " << GetStateDescription(state) << " already set... ";
|
---|
846 | if (!cmd.empty())
|
---|
847 | Out() << "'" << cmd << "' ignored.";
|
---|
848 | Out() << endl;
|
---|
849 | return "";
|
---|
850 | }
|
---|
851 |
|
---|
852 | const int old = fCurrentState;
|
---|
853 |
|
---|
854 | const string nold = GetStateDescription(old);
|
---|
855 | const string nnew = GetStateDescription(state);
|
---|
856 |
|
---|
857 | string msg = nnew + " " + txt;
|
---|
858 | if (!cmd.empty())
|
---|
859 | msg += " (" + cmd + ")";
|
---|
860 |
|
---|
861 | fCurrentState = state;
|
---|
862 |
|
---|
863 | // State might have changed already again...
|
---|
864 | // Not very likely, but possible. That's why state is used
|
---|
865 | // instead of fCurrentState.
|
---|
866 |
|
---|
867 | ostringstream str;
|
---|
868 | str << "State Transition from " << nold << " to " << nnew << " (" << txt;
|
---|
869 | if (!cmd.empty())
|
---|
870 | str << ": " << cmd;
|
---|
871 | str << ")";
|
---|
872 | Message(str);
|
---|
873 |
|
---|
874 | return msg;
|
---|
875 | }
|
---|
876 |
|
---|
877 | // --------------------------------------------------------------------------
|
---|
878 | //
|
---|
879 | //! This function handles a new state issued by one of the event handlers.
|
---|
880 | //!
|
---|
881 | //! @param newstate
|
---|
882 | //! A possible new state
|
---|
883 | //!
|
---|
884 | //! @param evt
|
---|
885 | //! A pointer to the event which was responsible for the state change,
|
---|
886 | //! NULL if no event was responsible.
|
---|
887 | //!
|
---|
888 | //! @param txt
|
---|
889 | //! Text which is issued if the current state has changed and the new
|
---|
890 | //! state is identical to the target state as stored in the event
|
---|
891 | //! reference, or when no alternative text was given, or the pointer to
|
---|
892 | //! evt is NULL.
|
---|
893 | //!
|
---|
894 | //! @param alt
|
---|
895 | //! An alternative text which is issues when the newstate of a state change
|
---|
896 | //! doesn't match the expected target state.
|
---|
897 | //!
|
---|
898 | //! @returns
|
---|
899 | //! false if newstate is kSM_FatalError, true otherwise
|
---|
900 | //
|
---|
901 | bool StateMachineImp::HandleNewState(int newstate, const EventImp *evt,
|
---|
902 | const char *txt, const char *alt)
|
---|
903 | {
|
---|
904 | if (newstate==kSM_FatalError)
|
---|
905 | return false;
|
---|
906 |
|
---|
907 | if (newstate==fCurrentState)
|
---|
908 | return true;
|
---|
909 |
|
---|
910 | if (!evt || !alt || newstate==evt->GetTargetState())
|
---|
911 | SetCurrentState(newstate, txt, evt ? evt->GetName() : "");
|
---|
912 | else
|
---|
913 | SetCurrentState(newstate, alt, evt->GetName());
|
---|
914 |
|
---|
915 | return true;
|
---|
916 | }
|
---|
917 |
|
---|
918 | // --------------------------------------------------------------------------
|
---|
919 | //
|
---|
920 | //! This is the event handler. Depending on the type of event it calles
|
---|
921 | //! the function associated with the event, the Transition() or
|
---|
922 | //! Configure() function.
|
---|
923 | //!
|
---|
924 | //! It first checks if the given even is valid in the current state. If
|
---|
925 | //! it is not valid the function returns with true.
|
---|
926 | //!
|
---|
927 | //! If it is valid, it is checked whether a function is associated with
|
---|
928 | //! the event. If this is the case, evt.Exec() is called and HandleNewState
|
---|
929 | //! called with its return value.
|
---|
930 | //!
|
---|
931 | //! If the event's target state is negative (unnamed Event) the Configure()
|
---|
932 | //! function is called with the event as argument and HandleNewState with
|
---|
933 | //! its returned new state.
|
---|
934 | //!
|
---|
935 | //! If the event's target state is 0 or positive (named Event) the
|
---|
936 | //! Transition() function is called with the event as argument and
|
---|
937 | //! HandleNewState with its returned new state.
|
---|
938 | //!
|
---|
939 | //! In all three cases the return value of HandleNewState is returned.
|
---|
940 | //!
|
---|
941 | //! Any of the three commands should usually return the current state
|
---|
942 | //! or (in case of the Transition() command) return the new state. However,
|
---|
943 | //! all three command can issue a state change by returning a new state.
|
---|
944 | //! However, this will just change the internal state. Any action which
|
---|
945 | //! is connected with the state change must have been executed already.
|
---|
946 | //!
|
---|
947 | //! @param evt
|
---|
948 | //! a reference to the event which should be handled
|
---|
949 | //!
|
---|
950 | //! @returns
|
---|
951 | //! false in case one of the commands changed the state to kSM_FataError,
|
---|
952 | //! true otherwise
|
---|
953 | //
|
---|
954 | bool StateMachineImp::HandleEvent(const EventImp &evt)
|
---|
955 | {
|
---|
956 | ostringstream out;
|
---|
957 | out << "Handle: " << evt.GetName() << "[" << evt.GetSize() << "]";
|
---|
958 | Debug(out);
|
---|
959 |
|
---|
960 | // Get the new state from the command
|
---|
961 | const int commandstate = evt.GetTargetState();
|
---|
962 |
|
---|
963 | // Check if the received command is allow in the current state
|
---|
964 | if (!evt.IsStateAllowed(fCurrentState))
|
---|
965 | {
|
---|
966 | ostringstream msg;
|
---|
967 | msg << evt.GetName() << ": Not allowed in state ";
|
---|
968 | msg << GetStateDescription() << "... ignored.";
|
---|
969 | Warn(msg);
|
---|
970 | return true;
|
---|
971 | }
|
---|
972 |
|
---|
973 | if (evt.HasFunc())
|
---|
974 | return HandleNewState(evt.ExecFunc(), &evt,
|
---|
975 | "by ExecFunc function-call");
|
---|
976 |
|
---|
977 | // Check if this is a configuration command (a command which
|
---|
978 | // intention is not to change the state of our state-machine
|
---|
979 | if (commandstate<0)
|
---|
980 | return HandleNewState(Configure(evt), &evt, "by Configure-command");
|
---|
981 | else
|
---|
982 | return HandleNewState(Transition(evt), &evt,
|
---|
983 | "by Transition-command (expected)",
|
---|
984 | "by Transition-command (unexpected)");
|
---|
985 |
|
---|
986 | // This is a fatal error, because it can never happen
|
---|
987 | return false;
|
---|
988 | }
|
---|
989 |
|
---|
990 | // --------------------------------------------------------------------------
|
---|
991 | //
|
---|
992 | //! This is the main loop, or what could be called the running state
|
---|
993 | //! machine. The flow diagram below shows what the loop is actually doing.
|
---|
994 | //! It's main purpose is to serialize command excecution and the main
|
---|
995 | //! loop in the state machine (e.g. the tracking loop)
|
---|
996 | //!
|
---|
997 | //! Leaving the loop can be forced by setting fExitRequested to another
|
---|
998 | //! value than zero. This is done automatically if dim's EXIT command
|
---|
999 | //! is received or can be forced by calling Stop().
|
---|
1000 | //!
|
---|
1001 | //! As long as no new command arrives the Execute() command is called
|
---|
1002 | //! continously. This should implement the current action which
|
---|
1003 | //! should be performed in the current state, e.g. calculating a
|
---|
1004 | //! new command value and sending it to the hardware.
|
---|
1005 | //!
|
---|
1006 | //! If a command is received it is put into the fifo by the commandHandler().
|
---|
1007 | //! The main loop now checks the fifo. If commands are in the fifo, it is
|
---|
1008 | //! checked whether the command is valid ithin this state or not. If it is
|
---|
1009 | //! not valid it is ignored. If it is valid the corresponding action
|
---|
1010 | //! is performed. This can either be a call to Configure() (when no state
|
---|
1011 | //! change is connected to the command) or Transition() (if the command
|
---|
1012 | //! involves a state change).
|
---|
1013 | //! In both cases areference to the received command (Command) is
|
---|
1014 | //! passed to the function. Note that after the functions have finished
|
---|
1015 | //! the command will go out of scope and be deleted.
|
---|
1016 | //!
|
---|
1017 | //! None of the commands should take to long for execution. Otherwise the
|
---|
1018 | //! response time of the main loop will become too slow.
|
---|
1019 | //!
|
---|
1020 | //! Any of the three commands should usually return the current state
|
---|
1021 | //! or (in case of the Transition() command) return the new state. However,
|
---|
1022 | //! all three command can issue a state change by returning a new state.
|
---|
1023 | //! However, this will just change the internal state. Any action which
|
---|
1024 | //! is connected with the state change must have been executed already.
|
---|
1025 | //!
|
---|
1026 | //!
|
---|
1027 | //!
|
---|
1028 | //! \dot
|
---|
1029 | //! digraph Run {
|
---|
1030 | //! node [ shape=record, fontname=Helvetica, fontsize=10 ];
|
---|
1031 | //! edge [ labelfontname=Helvetica, labelfontsize=8 ];
|
---|
1032 | //! start0 [ label="Run()" style="rounded"];
|
---|
1033 | //! start1 [ label="fExitRequested=0\nfRunning=true\nSetCurrentState(kSM_Ready)"];
|
---|
1034 | //! cond1 [ label="Is fExitRequested==0?"];
|
---|
1035 | //! exec [ label="HandleNewState(Execute())"];
|
---|
1036 | //! fifo [ label="Any event in FIFO?"];
|
---|
1037 | //! get [ label="Get event from FIFO\n Is event allowed within the current state?" ];
|
---|
1038 | //! handle [ label="HandleEvent()" ];
|
---|
1039 | //! exit1 [ label="fRunning=false\nSetCurrentState(kSM_FatalError)\n return -1" style="rounded"];
|
---|
1040 | //! exit2 [ label="fRunning=false\nSetCurrentState(kSM_NotReady)\n return fExitRequested-1" style="rounded"];
|
---|
1041 | //!
|
---|
1042 | //! start0 -> start1 [ weight=8 ];
|
---|
1043 | //! start1 -> cond1 [ weight=8 ];
|
---|
1044 | //!
|
---|
1045 | //! cond1:e -> exit2:n [ taillabel="true" ];
|
---|
1046 | //! cond1 -> exec [ taillabel="false" weight=8 ];
|
---|
1047 | //!
|
---|
1048 | //! exec -> fifo [ taillabel="true" weight=8 ];
|
---|
1049 | //! exec:e -> exit1:e [ taillabel="false" ];
|
---|
1050 | //!
|
---|
1051 | //! fifo -> cond1 [ taillabel="false" ];
|
---|
1052 | //! fifo -> get [ taillabel="true" weight=8 ];
|
---|
1053 | //!
|
---|
1054 | //! get -> handle [ taillabel="true" ];
|
---|
1055 | //!
|
---|
1056 | //! handle:s -> exit1:n [ taillabel="false" weight=8 ];
|
---|
1057 | //! handle -> cond1 [ taillabel="true" ];
|
---|
1058 | //! }
|
---|
1059 | //! \enddot
|
---|
1060 | //!
|
---|
1061 | //! @param dummy
|
---|
1062 | //! If this parameter is set to treu then no action is executed
|
---|
1063 | //! and now events are dispatched from the event list. It is usefull
|
---|
1064 | //! if functions are assigned directly to any event to simulate
|
---|
1065 | //! a running loop (e.g. block until Stop() was called or fExitRequested
|
---|
1066 | //! was set by an EXIT command. If dummy==true, fRunning is not set
|
---|
1067 | //! to true to allow handling events directly from the event handler.
|
---|
1068 | //!
|
---|
1069 | //! @returns
|
---|
1070 | //! In the case of a a fatal error -1 is returned, fExitRequested-1 in all
|
---|
1071 | //! other cases (This corresponds to the exit code either received by the
|
---|
1072 | //! EXIT event or given to the Stop() function)
|
---|
1073 | //!
|
---|
1074 | //! @todo Fix docu (kSM_SetReady, HandleEvent)
|
---|
1075 | //
|
---|
1076 | int StateMachineImp::Run(bool dummy)
|
---|
1077 | {
|
---|
1078 | if (fCurrentState>=kSM_Ready)
|
---|
1079 | {
|
---|
1080 | Error("Run() can only be called in the NotReady state.");
|
---|
1081 | return -1;
|
---|
1082 | }
|
---|
1083 |
|
---|
1084 | if (!fExitRequested)
|
---|
1085 | {
|
---|
1086 | fRunning = !dummy;
|
---|
1087 |
|
---|
1088 | SetCurrentState(kSM_Ready, "by Run()");
|
---|
1089 |
|
---|
1090 | while (!fExitRequested)
|
---|
1091 | {
|
---|
1092 | usleep(1);
|
---|
1093 | if (dummy)
|
---|
1094 | continue;
|
---|
1095 |
|
---|
1096 | // Execute a step in the current state of the state machine
|
---|
1097 | if (!HandleNewState(Execute(), "by Execute-command"))
|
---|
1098 | break;
|
---|
1099 |
|
---|
1100 | // If the command stack is empty go on with processing in the
|
---|
1101 | // current state
|
---|
1102 | if (IsQueueEmpty())
|
---|
1103 | continue;
|
---|
1104 |
|
---|
1105 | // Pop the next command which arrived from the stack
|
---|
1106 | const auto_ptr<Event> cmd(PopEvent());
|
---|
1107 |
|
---|
1108 | if (!HandleEvent(*cmd))
|
---|
1109 | break;
|
---|
1110 | }
|
---|
1111 |
|
---|
1112 | fRunning = false;
|
---|
1113 |
|
---|
1114 | if (!fExitRequested)
|
---|
1115 | {
|
---|
1116 | Fatal("Fatal Error occured... shutting down.");
|
---|
1117 | return -1;
|
---|
1118 | }
|
---|
1119 |
|
---|
1120 | SetCurrentState(kSM_NotReady, "due to return from Run().");
|
---|
1121 | }
|
---|
1122 |
|
---|
1123 | const int exitcode = fExitRequested-1;
|
---|
1124 |
|
---|
1125 | // Prepare for next call
|
---|
1126 | fExitRequested = 0;
|
---|
1127 |
|
---|
1128 | return exitcode;
|
---|
1129 | }
|
---|
1130 |
|
---|
1131 | // --------------------------------------------------------------------------
|
---|
1132 | //
|
---|
1133 | //! This function can be called to stop the loop of a running state machine.
|
---|
1134 | //! Run() will then return with a return value corresponding to the value
|
---|
1135 | //! given as argument.
|
---|
1136 | //!
|
---|
1137 | //! Note that this is a dangerous operation, because as soon as one of the
|
---|
1138 | //! three state machine commands returns (Execute(), Configure() and
|
---|
1139 | //! Transition()) the loop will be left and Run(9 will return. The program
|
---|
1140 | //! is then responsible of correctly cleaning up the mess which might be left
|
---|
1141 | //! behind.
|
---|
1142 | //!
|
---|
1143 | //! @param code
|
---|
1144 | //! int with which Run() should return when returning.
|
---|
1145 | //
|
---|
1146 | void StateMachineImp::Stop(int code)
|
---|
1147 | {
|
---|
1148 | fExitRequested = code+1;
|
---|
1149 | }
|
---|