+++ /dev/null
-/*! ========================================================================
-** Extended Template and Library
-** State Machine Abstraction Class Implementation
-** $Id$
-**
-** Copyright (c) 2002 Robert B. Quattlebaum Jr.
-** Copyright (c) 2008 Chris Moore
-**
-** This package is free software; you can redistribute it and/or
-** modify it under the terms of the GNU General Public License as
-** published by the Free Software Foundation; either version 2 of
-** the License, or (at your option) any later version.
-**
-** This package is distributed in the hope that it will be useful,
-** but WITHOUT ANY WARRANTY; without even the implied warranty of
-** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-** General Public License for more details.
-**
-** === N O T E S ===========================================================
-**
-** ========================================================================= */
-
-/* === S T A R T =========================================================== */
-
-#ifndef __ETL__SMACH_H_
-#define __ETL__SMACH_H_
-
-/* === H E A D E R S ======================================================= */
-
-#include <vector>
-#include <algorithm>
-#include <stdexcept>
-#include "_mutex_null.h"
-#include "_misc.h"
-
-/* === M A C R O S ========================================================= */
-
-#define SMACH_STATE_STACK_SIZE (32)
-
-#ifdef _MSC_VER
-#pragma warning (disable:4786)
-#pragma warning (disable:4290) // MSVC6 doesn't like function declarations with exception specs
-#endif
-
-//#define ETL_MUTEX_LOCK() _mutex::lock lock(mutex)
-#define ETL_MUTEX_LOCK()
-
-/* === T Y P E D E F S ===================================================== */
-
-/* === C L A S S E S & S T R U C T S ======================================= */
-
-_ETL_BEGIN_NAMESPACE
-
-/*! ========================================================================
-** \class smach
-** \brief Templatized State Machine
-**
-** A more detailed description needs to be written.
-*/
-template <typename CON, typename K=int, typename M=mutex_null>
-class smach
-{
-public:
-
- typedef K event_key;
- typedef M _mutex;
- typedef CON context_type;
-
-
- struct egress_exception { };
- struct pop_exception { };
-
-
- //! Result type for event processing
- enum event_result
- {
- // These values are returned by the event
- // handlers cast to state pointers.
- RESULT_ERROR, //!< General error or malfunction
- RESULT_OK, //!< Event has been processed
- RESULT_ACCEPT, //!< The event has been explicitly accepted.
- RESULT_REJECT, //!< The event has been explicitly rejected.
-
- RESULT_END //!< Not a valid result
- };
-
- //template<typename T> class state;
-
- //! Event base class
- struct event
- {
- event_key key;
-
- event() { }
- event(const event_key& key):key(key) { }
-
- operator event_key()const { return key; }
- };
-
- //! Event definition class
- template<typename T>
- class event_def_internal
- {
- // List our friends
- friend class smach;
- //friend class state<T>;
-
- public:
- typedef T state_context_type;
-
- //! Event function type
- typedef event_result (T::*funcptr)(const event&);
-
- //private:
-
- event_key id; //<! Event ID
- funcptr handler; //<! Pointer event handler
-
- public:
-
- //! Less-than operator for sorting. Based on event_key value.
- bool operator<(const event_def_internal &rhs)const
- { return id<rhs.id; }
-
- //! Equal-to operator. Based on event_key value.
- bool operator==(const event_def_internal &rhs)const
- { return id==rhs.id; }
-
- //! Less-than operator for finding.
- bool operator<(const event_key &rhs)const
- { return id<rhs; }
-
- //! Equal-to operator. Based on event_key value.
- bool operator==(const event_key &rhs)const
- { return id==rhs; }
-
- //! Trivial Constructor
- event_def_internal() { }
-
- //! Constructor for creating an event_def_internal from the given key and function reference.
- event_def_internal(event_key a, funcptr b):id(a),handler(b) { }
-
- //! Copy constructor
- event_def_internal(const event_def_internal &x):id(x.id),handler(x.handler) { }
-
- };
-
- class state_base
- {
- // Our parent is our friend
- friend class smach;
- public:
- virtual ~state_base() { }
-
- virtual void* enter_state(context_type* machine_context)const=0;
-
- virtual bool leave_state(void* state_context)const=0;
-
- virtual event_result process_event(void* state_context,const event& id)const=0;
-
- virtual const char *get_name() const=0;
- };
-
- //! State class
- template<typename T>
- class state : public state_base
- {
- // Our parent is our friend
- friend class smach;
-
- public:
- typedef event_def_internal<T> event_def;
- typedef T state_context_type;
-
-
- private:
-
- std::vector<event_def> event_list;
-
- smach *nested; //! Nested machine
- event_key low,high; //! Lowest and Highest event values
- const char *name; //! Name of the state
- typename event_def::funcptr default_handler; //! Default handler for unknown key
-
- public:
-
- //! Constructor
- state(const char *n, smach* nest=0):
- nested(nest),name(n),default_handler(NULL)
- { }
-
- virtual ~state() { }
-
- //! Setup a nested state machine
- /*! A more detailed explanation needs to be written */
- void set_nested_machine(smach *sm) { nested=sm; }
-
- //! Sets the default handler
- void set_default_handler(const typename event_def::funcptr &x) { default_handler=x; }
-
- //! Returns given the name of the state
- virtual const char *get_name() const { return name; }
-
- state_context_type& get_context(smach& machine)
- {
- state_context_type *context(dynamic_cast<state_context_type*>(machine.state_context));
- if(context)
- return context;
-
- }
-
- //! Adds an event_def onto the list and then make sure it is sorted correctly.
- void
- insert(const event_def &x)
- {
- // If this is our first event_def,
- // setup the high and low values.
- if(!event_list.size())
- low=high=x.id;
-
- // Sort the event_def onto the list
- event_list.push_back(x);
- sort(event_list.begin(),event_list.end());
-
- // Update the low and high markers
- if(x.id<low)
- low=x.id;
- if(high<x.id)
- high=x.id;
- }
-
- typename std::vector<event_def>::iterator find(const event_key &x) { return binary_find(event_list.begin(),event_list.end(),x); }
- typename std::vector<event_def>::const_iterator find(const event_key &x)const { return binary_find(event_list.begin(),event_list.end(),x); }
-
- protected:
-
- virtual void* enter_state(context_type* machine_context)const
- {
- return new state_context_type(machine_context);
- }
-
- virtual bool leave_state(void* x)const
- {
- state_context_type* state_context(reinterpret_cast<state_context_type*>(x));
- delete state_context;
- return true;
- }
-
- virtual event_result
- process_event(void* x,const event& id)const
- {
- state_context_type* state_context(reinterpret_cast<state_context_type*>(x));
-
- // Check for nested machine in state
- if(nested)
- {
- const event_result ret(nested->process_event(id));
- if(ret!=RESULT_OK)
- return ret;
- }
-
- // Quick test to make sure that the
- // given event is in the state
- if(id.key<low || high<id.key)
- return RESULT_OK;
-
- // Look for the event
- typename std::vector<event_def>::const_iterator iter(find(id.key));
-
- // If search results were negative, fail.
- if(iter->id!=id.key)
- return RESULT_OK;
-
- // Execute event function
- event_result ret((state_context->*(iter->handler))(id));
-
- if(ret==RESULT_OK && default_handler)
- ret=(state_context->*(default_handler))(id);
-
- return ret;
- }
- };
-
-private:
-
- // Machine data
- const state_base* curr_state; //!< Current state of the machine
- smach* child; //!< Child machine
-
-public: // this really should be private
- void* state_context; //!< State Context
-private:
-
- context_type* machine_context; //!< Machine Context
-
- const state_base* default_state;
- void* default_context;
-
-#ifdef ETL_MUTEX_LOCK
- _mutex mutex;
-#endif
-
- //! State stack data
- const state_base* state_stack[SMACH_STATE_STACK_SIZE];
- void* state_context_stack[SMACH_STATE_STACK_SIZE];
- int states_on_stack;
-
-public:
-
- //! Gets the name of the currently active state
- const char *
- get_state_name()const
- {
-#ifdef ETL_MUTEX_LOCK
- ETL_MUTEX_LOCK();
-#endif
- if(curr_state)
- return curr_state->get_name();
- if(default_state)
- return default_state->get_name();
- return 0;
- }
-
- //! Determines if a given event result is an error
- /*! This function allows us to quickly see
- if an event_result contained an error */
- static bool
- event_error(const event_result &rhs)
- { return rhs<=RESULT_ERROR; }
-
- bool
- set_default_state(const state_base *nextstate)
- {
-#ifdef ETL_MUTEX_LOCK
- ETL_MUTEX_LOCK();
-#endif
- // Keep track of the current state unless
- // the state switch fails
- const state_base *prev_state=default_state;
-
- // If we are already in a state, leave it and
- // collapse the state stack
- if(default_state)
- default_state->leave_state(default_context);
-
- // Set this as our current state
- default_state=nextstate;
- default_context=0;
-
- // Attempt to enter the state
- if(default_state)
- {
- default_context=default_state->enter_state(machine_context);
- if(default_context)
- return true;
- }
- else
- return true;
-
- // We failed, so attempt to return to previous state
- default_state=prev_state;
-
- // If we had a previous state, enter it
- if(default_state)
- default_context=default_state->enter_state(machine_context);
-
- // At this point we are not in the
- // requested state, so return failure
- return false;
- }
-
- //! Leaves the current state
- /*! Effectively makes the state_depth() function return zero. */
- bool
- egress()
- {
-#ifdef ETL_MUTEX_LOCK
- ETL_MUTEX_LOCK();
-#endif
-
- // Pop all states off the state stack
- while(states_on_stack) pop_state();
-
- // If we are not in a state, then I guess
- // we were successful.
- if(!curr_state)
- return true;
-
- // Grab the return value from the exit function
- bool ret=true;
-
- const state_base* old_state=curr_state;
- void *old_context=state_context;
-
- // Clear out the current state and its state_context
- curr_state=0;state_context=0;
-
- // Leave the state
- return old_state->leave_state(old_context);
-
- return ret;
- }
-
- //! State entry function
- /*! Attempts to enter the given state,
- popping off all states on the stack
- in the process. */
- bool
- enter(const state_base *nextstate)
- {
-#ifdef ETL_MUTEX_LOCK
- ETL_MUTEX_LOCK();
-#endif
-
- // Keep track of the current state unless
- // the state switch fails
- const state_base *prev_state=curr_state;
-
- // If we are already in a state, leave it and
- // collapse the state stack
- if(curr_state)
- egress();
-
- // Set this as our current state
- curr_state=nextstate;
- state_context=0;
-
- // Attempt to enter the state
- state_context=curr_state->enter_state(machine_context);
- if(state_context)
- return true;
-
- // We failed, so attempt to return to previous state
- curr_state=prev_state;
-
- // If we had a previous state, enter it
- if(curr_state)
- state_context=curr_state->enter_state(machine_context);
-
- // At this point we are not in the
- // requested state, so return failure
- return false;
- }
-
- //! Pushes state onto state stack
- /*! This allows you to enter a state without
- leaving your current state.
- \param nextstate Pointer to the state to enter
- \sa pop_state()
- */
- bool
- push_state(const state_base *nextstate)
- {
-#ifdef ETL_MUTEX_LOCK
- ETL_MUTEX_LOCK();
-#endif
-
- // If there are not enough slots, then throw something.
- if(states_on_stack==SMACH_STATE_STACK_SIZE)
- throw(std::overflow_error("smach<>::push_state(): state stack overflow!"));
-
- // If there is no current state, nor anything on stack,
- // just go ahead and enter the given state.
- if(!curr_state && !states_on_stack)
- return enter(nextstate);
-
- // Push the current state onto the stack
- state_stack[states_on_stack]=curr_state;
- state_context_stack[states_on_stack++]=state_context;
-
- // Make the next state the current state
- curr_state=nextstate;
-
- // Try to enter the next state
- state_context=curr_state->enter_state(machine_context);
- if(state_context)
- return true;
-
- // Unable to push state, return to old one
- curr_state=state_stack[--states_on_stack];
- state_context=state_context_stack[states_on_stack];
- return false;
- }
-
- //! Pops state off of state stack
- /*! Decreases state depth */
- void
- pop_state()
- {
-#ifdef ETL_MUTEX_LOCK
- ETL_MUTEX_LOCK();
-#endif
-
- // If we aren't in a state, then there is nothing
- // to do.
- if(!curr_state)
- throw(std::underflow_error("smach<>::pop_state(): stack is empty!"));
-
- if(states_on_stack)
- {
- const state_base* old_state=curr_state;
- void *old_context=state_context;
-
- // Pop previous state off of stack
- --states_on_stack;
- curr_state=state_stack[states_on_stack];
- state_context=state_context_stack[states_on_stack];
-
- old_state->leave_state(old_context);
- }
- else // If there are no states on stack, just egress
- egress();
- }
-
- //! State Machine Constructor
- /*! A more detailed description needs to be written */
- smach(context_type* machine_context=0):
- curr_state(0),
- child(0),
- state_context(0),
- machine_context(machine_context),
- default_state(0),
- default_context(0),
- states_on_stack(0)
- { }
-
- //! The destructor
- ~smach()
- {
- egress();
-
- if(default_state)
- default_state->leave_state(default_context);
- }
-
- //! Sets up a child state machine
- /*! A child state machine runs in parallel with
- its parent, and gets event priority. This
- mechanism is useful in cases where an inherited
- object has its own state machine. */
- void set_child(smach *x)
- {
-#ifdef ETL_MUTEX_LOCK
- ETL_MUTEX_LOCK();
-#endif
- child=x;
- }
-
- //! Returns the number states currently active
- int
- state_depth()
- { return curr_state?states_on_stack+1:0; }
-
- event_result
- process_event(const event_key& id) { return process_event(event(id)); }
-
- //! Process an event
- event_result
- process_event(const event& id)
- {
-#ifdef ETL_MUTEX_LOCK
- ETL_MUTEX_LOCK();
-#endif
-
- event_result ret(RESULT_OK);
-
- // Check for child machine
- if(child)
- {
- ret=child->process_event(id);
- if(ret!=RESULT_OK)
- return ret;
- }
-
- try
- {
- if(curr_state)
- ret=curr_state->process_event(state_context,id);
-
- if(ret==RESULT_OK)
- return default_state->process_event(default_context,id);
-
- return ret;
- }
- catch(egress_exception) { return egress()?RESULT_ACCEPT:RESULT_ERROR; }
- catch(pop_exception) { pop_state(); return RESULT_ACCEPT; }
- catch(const state_base* state) { return enter(state)?RESULT_ACCEPT:RESULT_ERROR; }
- }
-
-}; // END of template class smach
-
-_ETL_END_NAMESPACE
-
-/* === E X T E R N S ======================================================= */
-
-/* === E N D =============================================================== */
-
-#endif