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