1 /*! ========================================================================
2 ** Extended Template and Library
3 ** State Machine Abstraction Class Implementation
6 ** Copyright (c) 2002 Robert B. Quattlebaum Jr.
7 ** Copyright (c) 2008 Chris Moore
9 ** This package is free software; you can redistribute it and/or
10 ** modify it under the terms of the GNU General Public License as
11 ** published by the Free Software Foundation; either version 2 of
12 ** the License, or (at your option) any later version.
14 ** This package is distributed in the hope that it will be useful,
15 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 ** General Public License for more details.
19 ** === N O T E S ===========================================================
21 ** ========================================================================= */
23 /* === S T A R T =========================================================== */
25 #ifndef __ETL__SMACH_H_
26 #define __ETL__SMACH_H_
28 /* === H E A D E R S ======================================================= */
33 #include "_mutex_null.h"
36 /* === M A C R O S ========================================================= */
38 #define SMACH_STATE_STACK_SIZE (32)
41 #pragma warning (disable:4786)
42 #pragma warning (disable:4290) // MSVC6 doesn't like function declarations with exception specs
45 //#define ETL_MUTEX_LOCK() _mutex::lock lock(mutex)
46 #define ETL_MUTEX_LOCK()
48 /* === T Y P E D E F S ===================================================== */
50 /* === C L A S S E S & S T R U C T S ======================================= */
54 /*! ========================================================================
56 ** \brief Templatized State Machine
58 ** A more detailed description needs to be written.
60 template <typename CON, typename K=int, typename M=mutex_null>
67 typedef CON context_type;
70 struct egress_exception { };
71 struct pop_exception { };
74 //! Result type for event processing
77 // These values are returned by the event
78 // handlers cast to state pointers.
79 RESULT_ERROR, //!< General error or malfunction
80 RESULT_OK, //!< Event has been processed
81 RESULT_ACCEPT, //!< The event has been explicitly accepted.
82 RESULT_REJECT, //!< The event has been explicitly rejected.
84 RESULT_END //!< Not a valid result
87 //template<typename T> class state;
95 event(const event_key& key):key(key) { }
97 operator event_key()const { return key; }
100 //! Event definition class
102 class event_def_internal
106 //friend class state<T>;
109 typedef T state_context_type;
111 //! Event function type
112 typedef event_result (T::*funcptr)(const event&);
116 event_key id; //<! Event ID
117 funcptr handler; //<! Pointer event handler
121 //! Less-than operator for sorting. Based on event_key value.
122 bool operator<(const event_def_internal &rhs)const
123 { return id<rhs.id; }
125 //! Equal-to operator. Based on event_key value.
126 bool operator==(const event_def_internal &rhs)const
127 { return id==rhs.id; }
129 //! Less-than operator for finding.
130 bool operator<(const event_key &rhs)const
133 //! Equal-to operator. Based on event_key value.
134 bool operator==(const event_key &rhs)const
137 //! Trivial Constructor
138 event_def_internal() { }
140 //! Constructor for creating an event_def_internal from the given key and function reference.
141 event_def_internal(event_key a, funcptr b):id(a),handler(b) { }
144 event_def_internal(const event_def_internal &x):id(x.id),handler(x.handler) { }
150 // Our parent is our friend
153 virtual ~state_base() { }
155 virtual void* enter_state(context_type* machine_context)const=0;
157 virtual bool leave_state(void* state_context)const=0;
159 virtual event_result process_event(void* state_context,const event& id)const=0;
161 virtual const char *get_name() const=0;
166 class state : public state_base
168 // Our parent is our friend
172 typedef event_def_internal<T> event_def;
173 typedef T state_context_type;
178 std::vector<event_def> event_list;
180 smach *nested; //! Nested machine
181 event_key low,high; //! Lowest and Highest event values
182 const char *name; //! Name of the state
183 typename event_def::funcptr default_handler; //! Default handler for unknown key
188 state(const char *n, smach* nest=0):
189 nested(nest),name(n),default_handler(NULL)
194 //! Setup a nested state machine
195 /*! A more detailed explanation needs to be written */
196 void set_nested_machine(smach *sm) { nested=sm; }
198 //! Sets the default handler
199 void set_default_handler(const typename event_def::funcptr &x) { default_handler=x; }
201 //! Returns given the name of the state
202 virtual const char *get_name() const { return name; }
204 state_context_type& get_context(smach& machine)
206 state_context_type *context(dynamic_cast<state_context_type*>(machine.state_context));
212 //! Adds an event_def onto the list and then make sure it is sorted correctly.
214 insert(const event_def &x)
216 // If this is our first event_def,
217 // setup the high and low values.
218 if(!event_list.size())
221 // Sort the event_def onto the list
222 event_list.push_back(x);
223 sort(event_list.begin(),event_list.end());
225 // Update the low and high markers
232 typename std::vector<event_def>::iterator find(const event_key &x) { return binary_find(event_list.begin(),event_list.end(),x); }
233 typename std::vector<event_def>::const_iterator find(const event_key &x)const { return binary_find(event_list.begin(),event_list.end(),x); }
237 virtual void* enter_state(context_type* machine_context)const
239 return new state_context_type(machine_context);
242 virtual bool leave_state(void* x)const
244 state_context_type* state_context(reinterpret_cast<state_context_type*>(x));
245 delete state_context;
250 process_event(void* x,const event& id)const
252 state_context_type* state_context(reinterpret_cast<state_context_type*>(x));
254 // Check for nested machine in state
257 const event_result ret(nested->process_event(id));
262 // Quick test to make sure that the
263 // given event is in the state
264 if(id.key<low || high<id.key)
267 // Look for the event
268 typename std::vector<event_def>::const_iterator iter(find(id.key));
270 // If search results were negative, fail.
274 // Execute event function
275 event_result ret((state_context->*(iter->handler))(id));
277 if(ret==RESULT_OK && default_handler)
278 ret=(state_context->*(default_handler))(id);
287 const state_base* curr_state; //!< Current state of the machine
288 smach* child; //!< Child machine
290 public: // this really should be private
291 void* state_context; //!< State Context
294 context_type* machine_context; //!< Machine Context
296 const state_base* default_state;
297 void* default_context;
299 #ifdef ETL_MUTEX_LOCK
304 const state_base* state_stack[SMACH_STATE_STACK_SIZE];
305 void* state_context_stack[SMACH_STATE_STACK_SIZE];
310 //! Gets the name of the currently active state
312 get_state_name()const
314 #ifdef ETL_MUTEX_LOCK
318 return curr_state->get_name();
320 return default_state->get_name();
324 //! Determines if a given event result is an error
325 /*! This function allows us to quickly see
326 if an event_result contained an error */
328 event_error(const event_result &rhs)
329 { return rhs<=RESULT_ERROR; }
332 set_default_state(const state_base *nextstate)
334 #ifdef ETL_MUTEX_LOCK
337 // Keep track of the current state unless
338 // the state switch fails
339 const state_base *prev_state=default_state;
341 // If we are already in a state, leave it and
342 // collapse the state stack
344 default_state->leave_state(default_context);
346 // Set this as our current state
347 default_state=nextstate;
350 // Attempt to enter the state
353 default_context=default_state->enter_state(machine_context);
360 // We failed, so attempt to return to previous state
361 default_state=prev_state;
363 // If we had a previous state, enter it
365 default_context=default_state->enter_state(machine_context);
367 // At this point we are not in the
368 // requested state, so return failure
372 //! Leaves the current state
373 /*! Effectively makes the state_depth() function return zero. */
377 #ifdef ETL_MUTEX_LOCK
381 // Pop all states off the state stack
382 while(states_on_stack) pop_state();
384 // If we are not in a state, then I guess
385 // we were successful.
389 // Grab the return value from the exit function
392 const state_base* old_state=curr_state;
393 void *old_context=state_context;
395 // Clear out the current state and its state_context
396 curr_state=0;state_context=0;
399 return old_state->leave_state(old_context);
404 //! State entry function
405 /*! Attempts to enter the given state,
406 popping off all states on the stack
409 enter(const state_base *nextstate)
411 #ifdef ETL_MUTEX_LOCK
415 // Keep track of the current state unless
416 // the state switch fails
417 const state_base *prev_state=curr_state;
419 // If we are already in a state, leave it and
420 // collapse the state stack
424 // Set this as our current state
425 curr_state=nextstate;
428 // Attempt to enter the state
429 state_context=curr_state->enter_state(machine_context);
433 // We failed, so attempt to return to previous state
434 curr_state=prev_state;
436 // If we had a previous state, enter it
438 state_context=curr_state->enter_state(machine_context);
440 // At this point we are not in the
441 // requested state, so return failure
445 //! Pushes state onto state stack
446 /*! This allows you to enter a state without
447 leaving your current state.
448 \param nextstate Pointer to the state to enter
452 push_state(const state_base *nextstate)
454 #ifdef ETL_MUTEX_LOCK
458 // If there are not enough slots, then throw something.
459 if(states_on_stack==SMACH_STATE_STACK_SIZE)
460 throw(std::overflow_error("smach<>::push_state(): state stack overflow!"));
462 // If there is no current state, nor anything on stack,
463 // just go ahead and enter the given state.
464 if(!curr_state && !states_on_stack)
465 return enter(nextstate);
467 // Push the current state onto the stack
468 state_stack[states_on_stack]=curr_state;
469 state_context_stack[states_on_stack++]=state_context;
471 // Make the next state the current state
472 curr_state=nextstate;
474 // Try to enter the next state
475 state_context=curr_state->enter_state(machine_context);
479 // Unable to push state, return to old one
480 curr_state=state_stack[--states_on_stack];
481 state_context=state_context_stack[states_on_stack];
485 //! Pops state off of state stack
486 /*! Decreases state depth */
490 #ifdef ETL_MUTEX_LOCK
494 // If we aren't in a state, then there is nothing
497 throw(std::underflow_error("smach<>::pop_state(): stack is empty!"));
501 const state_base* old_state=curr_state;
502 void *old_context=state_context;
504 // Pop previous state off of stack
506 curr_state=state_stack[states_on_stack];
507 state_context=state_context_stack[states_on_stack];
509 old_state->leave_state(old_context);
511 else // If there are no states on stack, just egress
515 //! State Machine Constructor
516 /*! A more detailed description needs to be written */
517 smach(context_type* machine_context=0):
521 machine_context(machine_context),
533 default_state->leave_state(default_context);
536 //! Sets up a child state machine
537 /*! A child state machine runs in parallel with
538 its parent, and gets event priority. This
539 mechanism is useful in cases where an inherited
540 object has its own state machine. */
541 void set_child(smach *x)
543 #ifdef ETL_MUTEX_LOCK
549 //! Returns the number states currently active
552 { return curr_state?states_on_stack+1:0; }
555 process_event(const event_key& id) { return process_event(event(id)); }
559 process_event(const event& id)
561 #ifdef ETL_MUTEX_LOCK
565 event_result ret(RESULT_OK);
567 // Check for child machine
570 ret=child->process_event(id);
578 ret=curr_state->process_event(state_context,id);
581 return default_state->process_event(default_context,id);
585 catch(egress_exception) { return egress()?RESULT_ACCEPT:RESULT_ERROR; }
586 catch(pop_exception) { pop_state(); return RESULT_ACCEPT; }
587 catch(const state_base* state) { return enter(state)?RESULT_ACCEPT:RESULT_ERROR; }
590 }; // END of template class smach
594 /* === E X T E R N S ======================================================= */
596 /* === E N D =============================================================== */