mirror of
https://github.com/ETLCPP/etl.git
synced 2026-11-09 14:49:53 +08:00
Experimental versions
This commit is contained in:
parent
6df2adf079
commit
7aeb8636c7
1
src/CreateFSM.bat
Normal file
1
src/CreateFSM.bat
Normal file
@ -0,0 +1 @@
|
||||
python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h
|
||||
1
src/CreateMessageRouter.bat
Normal file
1
src/CreateMessageRouter.bat
Normal file
@ -0,0 +1 @@
|
||||
python -m cogapp -d -e -omessage_router.h -DHandlers=16 message_router_generator.h
|
||||
@ -1,339 +0,0 @@
|
||||
|
||||
#ifndef __ETL_FSM__
|
||||
#define __ETL_FSM__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "../array.h"
|
||||
#include "../nullptr.h"
|
||||
#include "../error_handler.h"
|
||||
#include "../exception.h"
|
||||
|
||||
#undef ETL_FILE
|
||||
#define ETL_FILE "34"
|
||||
|
||||
namespace etl
|
||||
{
|
||||
#if !defined(ETL_FSM_STATE_ID_TYPE)
|
||||
typedef uint_least8_t fsm_state_id_t;
|
||||
#else
|
||||
typedef ETL_FSM_STATE_ID_TYPE fsm_state_id_t;
|
||||
#endif
|
||||
|
||||
#if !defined(ETL_FSM_EVENT_ID_TYPE)
|
||||
typedef uint_least8_t fsm_event_id_t;
|
||||
#else
|
||||
typedef ETL_FSM_STATE_ID_TYPE fsm_event_id_t;
|
||||
#endif
|
||||
|
||||
//***************************************************************************
|
||||
/// Base exception class for FSM.
|
||||
//***************************************************************************
|
||||
class fsm_exception : public etl::exception
|
||||
{
|
||||
public:
|
||||
|
||||
fsm_exception(string_type what, string_type file_name, numeric_type line_number)
|
||||
: etl::exception(what, file_name, line_number)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Exception for null state pointer.
|
||||
//***************************************************************************
|
||||
class fsm_nullstate_exception : public etl::fsm_exception
|
||||
{
|
||||
public:
|
||||
|
||||
fsm_nullstate_exception(string_type file_name, numeric_type line_number)
|
||||
: etl::fsm_exception(ETL_ERROR_TEXT("fsm:null state", ETL_FILE"A"), file_name, line_number)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Exception for invalid state id.
|
||||
//***************************************************************************
|
||||
class fsm_state_id_exception : public etl::fsm_exception
|
||||
{
|
||||
public:
|
||||
|
||||
fsm_state_id_exception(string_type file_name, numeric_type line_number)
|
||||
: etl::fsm_exception(ETL_ERROR_TEXT("fsm:state id", ETL_FILE"B"), file_name, line_number)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Interface class for FSM events.
|
||||
//***************************************************************************
|
||||
class ifsm_event
|
||||
{
|
||||
public:
|
||||
|
||||
//*******************************************
|
||||
/// Gets the id for this event.
|
||||
//*******************************************
|
||||
etl::fsm_event_id_t get_event_id() const
|
||||
{
|
||||
return event_id;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
ifsm_event(etl::fsm_event_id_t event_id_)
|
||||
: event_id(event_id_)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// The event id.
|
||||
const etl::fsm_event_id_t event_id;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Base class for FSM events.
|
||||
//***************************************************************************
|
||||
template <const etl::fsm_event_id_t EVENT_ID_>
|
||||
class fsm_event : public etl::ifsm_event
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
EVENT_ID = EVENT_ID_
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
fsm_event()
|
||||
: ifsm_event(EVENT_ID)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Interface class for FSM states.
|
||||
//***************************************************************************
|
||||
class ifsm_state
|
||||
{
|
||||
public:
|
||||
|
||||
//*******************************************
|
||||
/// Gets the id for this state.
|
||||
//*******************************************
|
||||
etl::fsm_state_id_t get_state_id() const
|
||||
{
|
||||
return state_id;
|
||||
}
|
||||
|
||||
virtual etl::fsm_state_id_t on_event(const etl::ifsm_event& event) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
ifsm_state(etl::fsm_state_id_t state_id_)
|
||||
: state_id(state_id_)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void on_enter_state() {}; // By default, do nothing.
|
||||
virtual void on_exit_state() {}; // By default, do nothing.
|
||||
|
||||
private:
|
||||
|
||||
// The state id.
|
||||
const etl::fsm_state_id_t state_id;
|
||||
|
||||
// Disabled.
|
||||
ifsm_state(const ifsm_state&);
|
||||
ifsm_state& operator =(const ifsm_state&);
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// To be COG generated.
|
||||
/// Base class for FSM states.
|
||||
//***************************************************************************
|
||||
template <typename TState, const etl::fsm_state_id_t STATE_ID_, typename T1, typename T2 = void>
|
||||
class fsm_state : public ifsm_state
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
STATE_ID = STATE_ID_
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
fsm_state()
|
||||
: ifsm_state(STATE_ID)
|
||||
{
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Top level event handler for the state.
|
||||
//*******************************************
|
||||
etl::fsm_state_id_t on_event(const etl::ifsm_event& event)
|
||||
{
|
||||
etl::fsm_state_id_t new_state_id;
|
||||
etl::fsm_event_id_t id = event.get_event_id();
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case T1::EVENT_ID: new_state_id = static_cast<TState&>(*this).on_event(static_cast<const T1&>(event)); break;
|
||||
case T2::EVENT_ID: new_state_id = static_cast<TState&>(*this).on_event(static_cast<const T2&>(event)); break;
|
||||
default: new_state_id = static_cast<TState&>(*this).on_unknown_event(event); break;
|
||||
}
|
||||
|
||||
return new_state_id;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// Disabled.
|
||||
fsm_state(const fsm_state&);
|
||||
fsm_state& operator =(const fsm_state&);
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
template <const etl::fsm_state_id_t MAX_STATES>
|
||||
class fsm
|
||||
{
|
||||
public:
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
fsm()
|
||||
: p_state(nullptr)
|
||||
{
|
||||
state_list.fill(nullptr);
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Starts the FSM.
|
||||
/// Can only be called once.
|
||||
/// Subsequent calls will do nothing.
|
||||
//*******************************************
|
||||
void start()
|
||||
{
|
||||
// Can only be started once.
|
||||
if (p_state == nullptr)
|
||||
{
|
||||
p_state = state_list[0];
|
||||
ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_nullstate_exception));
|
||||
|
||||
p_state->on_enter_state();
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Adds a state to the FSM.
|
||||
/// If the state has the same id as one already added
|
||||
/// then the current state will be overwritten.
|
||||
//*******************************************
|
||||
void add_state(etl::ifsm_state& state)
|
||||
{
|
||||
ETL_ASSERT(state.get_state_id() < MAX_STATES, ETL_ERROR(etl::fsm_state_id_exception));
|
||||
state_list[state.get_state_id()] = &state;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Top level event handler for the FSM.
|
||||
//*******************************************
|
||||
void on_event(const etl::ifsm_event& event)
|
||||
{
|
||||
etl::fsm_state_id_t next_state_id = p_state->on_event(event);
|
||||
ETL_ASSERT(next_state_id < MAX_STATES, ETL_ERROR(etl::fsm_state_id_exception));
|
||||
|
||||
// Have we changed state?
|
||||
if (next_state_id != p_state->get_state_id())
|
||||
{
|
||||
p_state->on_exit_state();
|
||||
|
||||
p_state = state_list[next_state_id];
|
||||
ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_nullstate_exception));
|
||||
|
||||
p_state->on_enter_state();
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Gets the current state id.
|
||||
//*******************************************
|
||||
etl::fsm_state_id_t get_state_id() const
|
||||
{
|
||||
return p_state->get_state_id();
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Gets a reference to the current state interface.
|
||||
//*******************************************
|
||||
ifsm_state& get_state()
|
||||
{
|
||||
return *p_state;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Gets a const reference to the current state interface.
|
||||
//*******************************************
|
||||
const ifsm_state& get_state() const
|
||||
{
|
||||
return *p_state;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
etl::ifsm_state* p_state; ///< A pointer to the current state.
|
||||
etl::array<etl::ifsm_state*, MAX_STATES> state_list; ///< The list of added states.
|
||||
|
||||
// Disabled.
|
||||
fsm(const fsm&);
|
||||
fsm& operator =(const fsm&);
|
||||
};
|
||||
}
|
||||
|
||||
#undef ETL_FILE
|
||||
|
||||
// class Start : public etl::fsm_event<0>
|
||||
// {
|
||||
|
||||
// };
|
||||
|
||||
// class Idle : public etl::fsm_state<Idle, 0, Start>
|
||||
// {
|
||||
// public:
|
||||
|
||||
// etl::fsm_state_id_t on_event(const Start& event)
|
||||
// {
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// etl::fsm_state_id_t on_unknown_event(const etl::ifsm_event& event)
|
||||
// {
|
||||
// return THIS_STATE_ID;
|
||||
// }
|
||||
// };
|
||||
|
||||
// etl::fsm<3> machine;
|
||||
|
||||
// void F()
|
||||
// {
|
||||
// Idle idle;
|
||||
|
||||
// machine.add_state(idle);
|
||||
// machine.start();
|
||||
// machine.on_event(Start());
|
||||
// }
|
||||
|
||||
#endif
|
||||
477
src/fsm_generator.h
Normal file
477
src/fsm_generator.h
Normal file
@ -0,0 +1,477 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
http://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2017 jwellbelove
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __ETL_FSM__
|
||||
#define __ETL_FSM__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "array.h"
|
||||
#include "nullptr.h"
|
||||
#include "error_handler.h"
|
||||
#include "exception.h"
|
||||
#include "user_type.h"
|
||||
|
||||
#undef ETL_FILE
|
||||
#define ETL_FILE "34"
|
||||
|
||||
namespace etl
|
||||
{
|
||||
#if !defined(ETL_FSM_STATE_ID_TYPE)
|
||||
typedef uint_least8_t fsm_state_id_t;
|
||||
#else
|
||||
typedef ETL_FSM_STATE_ID_TYPE fsm_state_id_t;
|
||||
#endif
|
||||
|
||||
#if !defined(ETL_FSM_EVENT_ID_TYPE)
|
||||
typedef uint_least8_t fsm_event_id_t;
|
||||
#else
|
||||
typedef ETL_FSM_STATE_ID_TYPE fsm_event_id_t;
|
||||
#endif
|
||||
|
||||
//***************************************************************************
|
||||
/// Base exception class for FSM.
|
||||
//***************************************************************************
|
||||
class fsm_exception : public etl::exception
|
||||
{
|
||||
public:
|
||||
|
||||
fsm_exception(string_type what, string_type file_name, numeric_type line_number)
|
||||
: etl::exception(what, file_name, line_number)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Exception for null state pointer.
|
||||
//***************************************************************************
|
||||
class fsm_null_state_exception : public etl::fsm_exception
|
||||
{
|
||||
public:
|
||||
|
||||
fsm_null_state_exception(string_type file_name, numeric_type line_number)
|
||||
: etl::fsm_exception(ETL_ERROR_TEXT("fsm:null state", ETL_FILE"A"), file_name, line_number)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Exception for invalid state id.
|
||||
//***************************************************************************
|
||||
class fsm_state_id_exception : public etl::fsm_exception
|
||||
{
|
||||
public:
|
||||
|
||||
fsm_state_id_exception(string_type file_name, numeric_type line_number)
|
||||
: etl::fsm_exception(ETL_ERROR_TEXT("fsm:state id", ETL_FILE"B"), file_name, line_number)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Exception for incompatible state list.
|
||||
//***************************************************************************
|
||||
class fsm_state_list_exception : public etl::fsm_exception
|
||||
{
|
||||
public:
|
||||
|
||||
fsm_state_list_exception(string_type file_name, numeric_type line_number)
|
||||
: etl::fsm_exception(ETL_ERROR_TEXT("fsm:state list", ETL_FILE"C"), file_name, line_number)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Interface class for FSM events.
|
||||
//***************************************************************************
|
||||
class ifsm_event
|
||||
{
|
||||
public:
|
||||
|
||||
//*******************************************
|
||||
/// Gets the id for this event.
|
||||
//*******************************************
|
||||
etl::fsm_event_id_t get_event_id() const
|
||||
{
|
||||
return event_id;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
ifsm_event(etl::fsm_event_id_t event_id_)
|
||||
: event_id(event_id_)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// The event id.
|
||||
const etl::fsm_event_id_t event_id;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Base class for FSM events.
|
||||
//***************************************************************************
|
||||
template <const etl::fsm_event_id_t EVENT_ID_>
|
||||
class fsm_event : public etl::ifsm_event
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
EVENT_ID = EVENT_ID_
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
fsm_event()
|
||||
: ifsm_event(EVENT_ID)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// Interface class for FSM states.
|
||||
//***************************************************************************
|
||||
class ifsm_state
|
||||
{
|
||||
public:
|
||||
|
||||
friend class fsm;
|
||||
|
||||
//*******************************************
|
||||
/// Gets the id for this state.
|
||||
//*******************************************
|
||||
etl::fsm_state_id_t get_state_id() const
|
||||
{
|
||||
return state_id;
|
||||
}
|
||||
|
||||
virtual etl::fsm_state_id_t on_event(const etl::ifsm_event& event) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
ifsm_state(etl::fsm_state_id_t state_id_)
|
||||
: state_id(state_id_)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void on_enter_state() {}; // By default, do nothing.
|
||||
virtual void on_exit_state() {}; // By default, do nothing.
|
||||
|
||||
private:
|
||||
|
||||
// The state id.
|
||||
const etl::fsm_state_id_t state_id;
|
||||
|
||||
// Disabled.
|
||||
ifsm_state(const ifsm_state&);
|
||||
ifsm_state& operator =(const ifsm_state&);
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
class fsm
|
||||
{
|
||||
public:
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
fsm()
|
||||
: p_state(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Set the states for the FSM
|
||||
//*******************************************
|
||||
template <typename TSize>
|
||||
void set_states(etl::ifsm_state** p_states, TSize size)
|
||||
{
|
||||
state_list = p_states;
|
||||
number_of_states = etl::fsm_state_id_t(size);
|
||||
|
||||
for (etl::fsm_state_id_t i = 0; i < size; ++i)
|
||||
{
|
||||
ETL_ASSERT((state_list[i] != nullptr), ETL_ERROR(etl::fsm_null_state_exception));
|
||||
}
|
||||
|
||||
bool ok = (number_of_states > 0) &&
|
||||
etl::is_sorted(state_list, state_list + number_of_states, fsm::CompareStateId());
|
||||
|
||||
ETL_ASSERT(ok, ETL_ERROR(etl::fsm_state_list_exception));
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Starts the FSM.
|
||||
/// Can only be called once.
|
||||
/// Subsequent calls will do nothing.
|
||||
//*******************************************
|
||||
void start()
|
||||
{
|
||||
// Can only be started once.
|
||||
if (p_state == nullptr)
|
||||
{
|
||||
p_state = state_list[0];
|
||||
ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception));
|
||||
|
||||
p_state->on_enter_state();
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Top level event handler for the FSM.
|
||||
//*******************************************
|
||||
void on_event(const etl::ifsm_event& event)
|
||||
{
|
||||
etl::fsm_state_id_t next_state_id = p_state->on_event(event);
|
||||
ETL_ASSERT(next_state_id < number_of_states, ETL_ERROR(etl::fsm_state_id_exception));
|
||||
|
||||
// Have we changed state?
|
||||
if (next_state_id != p_state->get_state_id())
|
||||
{
|
||||
p_state->on_exit_state();
|
||||
|
||||
p_state = state_list[next_state_id];
|
||||
ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception));
|
||||
|
||||
p_state->on_enter_state();
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Gets the current state id.
|
||||
//*******************************************
|
||||
etl::fsm_state_id_t get_state_id() const
|
||||
{
|
||||
ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception));
|
||||
return p_state->get_state_id();
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Gets a reference to the current state interface.
|
||||
//*******************************************
|
||||
ifsm_state& get_state()
|
||||
{
|
||||
ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception));
|
||||
return *p_state;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Gets a const reference to the current state interface.
|
||||
//*******************************************
|
||||
const ifsm_state& get_state() const
|
||||
{
|
||||
ETL_ASSERT(p_state != nullptr, ETL_ERROR(etl::fsm_null_state_exception));
|
||||
return *p_state;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Checks if the FSM has been started.
|
||||
//*******************************************
|
||||
bool is_started() const
|
||||
{
|
||||
return p_state != nullptr;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Reset the FSM to pre-started state.
|
||||
//*******************************************
|
||||
void reset()
|
||||
{
|
||||
p_state = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
struct CompareStateId
|
||||
{
|
||||
bool operator()(etl::ifsm_state* lhs, etl::ifsm_state* rhs)
|
||||
{
|
||||
return lhs->get_state_id() < rhs->get_state_id();
|
||||
}
|
||||
};
|
||||
|
||||
etl::ifsm_state* p_state; ///< A pointer to the current state.
|
||||
etl::ifsm_state** state_list; ///< The list of added states.
|
||||
etl::fsm_state_id_t number_of_states; ///< The number of states.
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// To generate to header file, run this at the command line.
|
||||
// Note: You will need Python and COG installed.
|
||||
//
|
||||
// python -m cogapp -d -e -ofsm.h -DHandlers=<n> fsm_generator.h
|
||||
// Where <n> is the number of messages to support.
|
||||
//
|
||||
// e.g.
|
||||
// To generate handlers for up to 16 events...
|
||||
// python -m cogapp -d -e -ofsm.h -DHandlers=16 fsm_generator.h
|
||||
//
|
||||
// See CreateFSM.bat
|
||||
//***************************************************************************
|
||||
|
||||
/*[[[cog
|
||||
import cog
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("// The code below has been auto generated. Do not manually edit.")
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("")
|
||||
################################################
|
||||
# The first definition for all of the events.
|
||||
################################################
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("// The definition for all %s message types." % Handlers)
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("template <typename TState, const etl::fsm_state_id_t STATE_ID_, ")
|
||||
cog.out(" ")
|
||||
for n in range(1, int(Handlers)):
|
||||
cog.out("typename T%s = void, " % n)
|
||||
if n % 4 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
cog.outl("typename T%s = void>" % Handlers)
|
||||
cog.outl("class fsm_state : public ifsm_state")
|
||||
cog.outl("{")
|
||||
cog.outl("public:")
|
||||
cog.outl("")
|
||||
|
||||
cog.outl(" friend class fsm;")
|
||||
cog.outl("")
|
||||
|
||||
cog.outl(" enum")
|
||||
cog.outl(" {")
|
||||
cog.outl(" STATE_ID = STATE_ID_")
|
||||
cog.outl(" };")
|
||||
cog.outl("")
|
||||
cog.outl(" fsm_state()")
|
||||
cog.outl(" : ifsm_state(STATE_ID)")
|
||||
cog.outl(" {")
|
||||
cog.outl(" }")
|
||||
cog.outl("")
|
||||
cog.outl(" etl::fsm_state_id_t on_event(const etl::ifsm_event& event)")
|
||||
cog.outl(" {")
|
||||
cog.outl(" etl::fsm_state_id_t new_state_id;")
|
||||
cog.outl(" etl::fsm_event_id_t event_id = event.get_event_id();")
|
||||
cog.outl("")
|
||||
cog.outl(" switch (event_id)")
|
||||
cog.outl(" {")
|
||||
for n in range(1, int(Handlers) + 1):
|
||||
cog.out(" case T%d::EVENT_ID:" % n)
|
||||
cog.out(" new_state_id = static_cast<TState*>(this)->on_event(static_cast<const T%d&>(event));" % n)
|
||||
cog.outl(" break;")
|
||||
cog.out(" default:")
|
||||
cog.out(" new_state_id = static_cast<TState*>(this)->on_event_unknown(event);")
|
||||
cog.outl(" break;")
|
||||
cog.outl(" }")
|
||||
cog.outl("")
|
||||
cog.outl(" return new_state_id;")
|
||||
cog.outl(" }")
|
||||
cog.outl("};")
|
||||
|
||||
####################################
|
||||
# All of the other specialisations.
|
||||
####################################
|
||||
for n in range(int(Handlers) - 1, 0, -1):
|
||||
cog.outl("")
|
||||
cog.outl("//***************************************************************************")
|
||||
if n == 1:
|
||||
cog.outl("// Specialisation for %d event type." % n)
|
||||
else:
|
||||
cog.outl("// Specialisation for %d event types." % n)
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("template <typename TState, const etl::fsm_state_id_t STATE_ID_, ")
|
||||
cog.out(" ")
|
||||
for t in range(1, n):
|
||||
cog.out("typename T%d, " % t)
|
||||
if t % 4 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
cog.outl("typename T%d>" % n)
|
||||
cog.out("class fsm_state<TState, STATE_ID_, ")
|
||||
for t in range(1, n + 1):
|
||||
cog.out("T%d, " % t)
|
||||
if t % 16 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
for t in range(n + 1, int(Handlers)):
|
||||
cog.out("void, ")
|
||||
if t % 16 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
cog.outl("void> : public ifsm_state")
|
||||
cog.outl("{")
|
||||
cog.outl("public:")
|
||||
|
||||
cog.outl(" friend class fsm;")
|
||||
cog.outl("")
|
||||
|
||||
cog.outl("")
|
||||
cog.outl(" enum")
|
||||
cog.outl(" {")
|
||||
cog.outl(" STATE_ID = STATE_ID_")
|
||||
cog.outl(" };")
|
||||
cog.outl("")
|
||||
cog.outl(" fsm_state()")
|
||||
cog.outl(" : ifsm_state(STATE_ID)")
|
||||
cog.outl(" {")
|
||||
cog.outl(" }")
|
||||
cog.outl("")
|
||||
cog.outl(" etl::fsm_state_id_t on_event(const etl::ifsm_event& event)")
|
||||
cog.outl(" {")
|
||||
cog.outl(" etl::fsm_state_id_t new_state_id;")
|
||||
cog.outl(" etl::fsm_event_id_t event_id = event.get_event_id();")
|
||||
cog.outl("")
|
||||
cog.outl(" switch (event_id)")
|
||||
cog.outl(" {")
|
||||
for n in range(1, n + 1):
|
||||
cog.out(" case T%d::EVENT_ID:" % n)
|
||||
cog.out(" new_state_id = static_cast<TState*>(this)->on_event(static_cast<const T%d&>(event));" % n)
|
||||
cog.outl(" break;")
|
||||
cog.out(" default:")
|
||||
cog.out(" new_state_id = static_cast<TState*>(this)->on_event_unknown(event);")
|
||||
cog.outl(" break;")
|
||||
cog.outl(" }")
|
||||
cog.outl("")
|
||||
cog.outl(" return new_state_id;")
|
||||
cog.outl(" }")
|
||||
cog.outl("};")
|
||||
]]]*/
|
||||
/*[[[end]]]*/
|
||||
}
|
||||
|
||||
#undef ETL_FILE
|
||||
|
||||
#endif
|
||||
1132
src/message_router.h
Normal file
1132
src/message_router.h
Normal file
File diff suppressed because it is too large
Load Diff
347
src/message_router_generator.h
Normal file
347
src/message_router_generator.h
Normal file
@ -0,0 +1,347 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
http://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2017 jwellbelove
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __ETL_MESSAGE_ROUTER__
|
||||
#define __ETL_MESSAGE_ROUTER__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace etl
|
||||
{
|
||||
//***************************************************************************
|
||||
class imessage
|
||||
{
|
||||
public:
|
||||
|
||||
typedef size_t id_t;
|
||||
|
||||
virtual ~imessage() {}
|
||||
virtual id_t get_message_id() const = 0;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
template <const size_t ID_>
|
||||
class message : public imessage
|
||||
{
|
||||
public:
|
||||
|
||||
enum
|
||||
{
|
||||
ID = ID_
|
||||
};
|
||||
|
||||
id_t get_message_id() const
|
||||
{
|
||||
return ID;
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
template <typename TReturn>
|
||||
class imessage_router
|
||||
{
|
||||
public:
|
||||
|
||||
typedef TReturn return_type;
|
||||
|
||||
virtual ~imessage_router() {}
|
||||
virtual TReturn receive(imessage_router& source, const imessage& message) = 0;
|
||||
|
||||
TReturn send_to(imessage_router<TReturn>& destination, const imessage& message)
|
||||
{
|
||||
return destination.receive(*this, message);
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
template <>
|
||||
class imessage_router<void>
|
||||
{
|
||||
public:
|
||||
|
||||
typedef void return_type;
|
||||
|
||||
virtual ~imessage_router() {}
|
||||
virtual void receive(imessage_router<void>& source, const imessage& message) = 0;
|
||||
|
||||
void send_to(imessage_router<void>& destination, const imessage& message)
|
||||
{
|
||||
destination.receive(*this, message);
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
template <typename TReturn>
|
||||
class null_message_router : public imessage_router<TReturn>
|
||||
{
|
||||
public:
|
||||
|
||||
null_message_router()
|
||||
: value(TReturn())
|
||||
{
|
||||
}
|
||||
|
||||
null_message_router(const TReturn& value)
|
||||
: value(value)
|
||||
{
|
||||
}
|
||||
|
||||
null_message_router(const null_message_router& other)
|
||||
: value(other.value)
|
||||
{
|
||||
}
|
||||
|
||||
null_message_router& operator ==(const null_message_router& other)
|
||||
: value(other.value)
|
||||
{
|
||||
}
|
||||
|
||||
TReturn receive(imessage_router<TReturn>& source, const imessage& message)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
TReturn value;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
template <>
|
||||
class null_message_router<void> : public imessage_router<void>
|
||||
{
|
||||
public:
|
||||
|
||||
void receive(imessage_router<void>& source, const imessage& message)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// To generate to header file, run this at the command line.
|
||||
// Note: You will need Python and COG installed.
|
||||
//
|
||||
// python -m cogapp -d -e -omessage_router.h -DHandlers=<n> message_router_generator.h
|
||||
// Where <n> is the number of messages to support.
|
||||
//
|
||||
// e.g.
|
||||
// To generate handlers for up to 16 messages...
|
||||
// python -m cogapp -d -e -omessage_router.h -DHandlers=16 message_router_generator.h
|
||||
//
|
||||
// See CreateMessageProcessor.bat
|
||||
//***************************************************************************
|
||||
|
||||
/*[[[cog
|
||||
import cog
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("// The code below has been auto generated. Do not manually edit.")
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("")
|
||||
################################################
|
||||
# The first definition for all of the messages.
|
||||
################################################
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("// The definition for all %s message types, returning TReturn." % Handlers)
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("template <typename TProcessor, typename TReturn,")
|
||||
cog.out(" ")
|
||||
for n in range(1, int(Handlers)):
|
||||
cog.out("typename T%s = void, " % n)
|
||||
if n % 4 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
cog.outl("typename T%s = void>" % Handlers)
|
||||
cog.outl("class message_router")
|
||||
cog.outl(" : public imessage_router<TReturn>")
|
||||
cog.outl("{")
|
||||
cog.outl("public:")
|
||||
cog.outl("")
|
||||
cog.outl(" TReturn receive(imessage_router<TReturn>& source, const imessage& msg)")
|
||||
cog.outl(" {")
|
||||
cog.outl(" const id_t id = msg.get_message_id();")
|
||||
cog.outl("")
|
||||
cog.outl(" switch (id)")
|
||||
cog.outl(" {")
|
||||
for n in range(1, int(Handlers) + 1):
|
||||
cog.out(" case T%d::ID:" % n)
|
||||
cog.out(" return static_cast<TProcessor*>(this)->on_receive(source, static_cast<const T%d&>(msg));" % n)
|
||||
cog.outl(" break;")
|
||||
cog.out(" default:")
|
||||
cog.out(" return static_cast<TProcessor*>(this)->on_receive_unknown(source, msg);")
|
||||
cog.outl(" break;")
|
||||
cog.outl(" }")
|
||||
cog.outl(" }")
|
||||
cog.outl("};")
|
||||
cog.outl("")
|
||||
|
||||
################################################
|
||||
# The first definition for all of the messages, with void return type.
|
||||
################################################
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("// The definition for all %s message types, returning void." % Handlers)
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("template <typename TProcessor,")
|
||||
cog.out(" ")
|
||||
for n in range(1, int(Handlers)):
|
||||
cog.out("typename T%s, " % n)
|
||||
if n % 4 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
cog.outl("typename T%s>" % Handlers)
|
||||
cog.out("class message_router<TProcessor, void, ")
|
||||
for t in range(1, int(Handlers)):
|
||||
cog.out("T%d, " % t)
|
||||
if t % 16 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
cog.outl("T%s>" % Handlers)
|
||||
cog.outl(" : public imessage_router<void>")
|
||||
cog.outl("{")
|
||||
cog.outl("public:")
|
||||
cog.outl("")
|
||||
cog.outl(" void receive(imessage_router<void>& source, const imessage& msg)")
|
||||
cog.outl(" {")
|
||||
cog.outl(" const id_t id = msg.get_message_id();")
|
||||
cog.outl("")
|
||||
cog.outl(" switch (id)")
|
||||
cog.outl(" {")
|
||||
for n in range(1, int(Handlers) + 1):
|
||||
cog.out(" case T%d::ID:" % n)
|
||||
cog.out(" static_cast<TProcessor*>(this)->on_receive(source, static_cast<const T%d&>(msg));" % n)
|
||||
cog.outl(" break;")
|
||||
cog.out(" default:")
|
||||
cog.out(" static_cast<TProcessor*>(this)->on_receive_unknown(source, msg);")
|
||||
cog.outl(" break;")
|
||||
cog.outl(" }")
|
||||
cog.outl(" }")
|
||||
cog.outl("};")
|
||||
|
||||
####################################
|
||||
# All of the other specialisations.
|
||||
####################################
|
||||
for n in range(int(Handlers) - 1, 0, -1):
|
||||
cog.outl("")
|
||||
cog.outl("//***************************************************************************")
|
||||
if n == 1:
|
||||
cog.outl("// Specialisation for %d message type, returning TReturn." % n)
|
||||
else:
|
||||
cog.outl("// Specialisation for %d message types, returning TReturn." % n)
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("template <typename TProcessor, typename TReturn, ")
|
||||
cog.out(" ")
|
||||
for t in range(1, n):
|
||||
cog.out("typename T%d, " % t)
|
||||
if t % 4 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
cog.outl("typename T%d>" % n)
|
||||
cog.out("class message_router<TProcessor, TReturn, ")
|
||||
for t in range(1, n + 1):
|
||||
cog.out("T%d, " % t)
|
||||
if t % 16 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
for t in range(n + 1, int(Handlers)):
|
||||
cog.out("void, ")
|
||||
if t % 16 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
cog.outl("void>")
|
||||
cog.outl(" : public imessage_router<TReturn>")
|
||||
cog.outl("{")
|
||||
cog.outl("public:")
|
||||
cog.outl("")
|
||||
cog.outl(" return_type receive(imessage_router<TReturn>& source, const imessage& msg)")
|
||||
cog.outl(" {")
|
||||
cog.outl(" const size_t id = msg.get_message_id();")
|
||||
cog.outl("")
|
||||
cog.outl(" switch (id)")
|
||||
cog.outl(" {")
|
||||
for t in range(1, n + 1):
|
||||
cog.out(" case T%d::ID:" % t)
|
||||
cog.out(" return static_cast<TProcessor*>(this)->on_receive(source, static_cast<const T%d&>(msg));" % t)
|
||||
cog.outl(" break;")
|
||||
cog.out(" default:")
|
||||
cog.out(" return static_cast<TProcessor*>(this)->on_receive_unknown(source, msg);")
|
||||
cog.outl(" break;")
|
||||
cog.outl(" }")
|
||||
cog.outl(" }")
|
||||
cog.outl("};")
|
||||
|
||||
cog.outl("")
|
||||
cog.outl("//***************************************************************************")
|
||||
if n == 1:
|
||||
cog.outl("// Specialisation for %d message type, returning void." % n)
|
||||
else:
|
||||
cog.outl("// Specialisation for %d message types, returning void." % n)
|
||||
cog.outl("//***************************************************************************")
|
||||
cog.outl("template <typename TProcessor, ")
|
||||
cog.out(" ")
|
||||
for t in range(1, n):
|
||||
cog.out("typename T%d, " % t)
|
||||
if t % 4 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
cog.outl("typename T%d>" % n)
|
||||
cog.out("class message_router<TProcessor, void, ")
|
||||
for t in range(1, n + 1):
|
||||
cog.out("T%d, " % t)
|
||||
if t % 16 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
for t in range(n + 1, int(Handlers)):
|
||||
cog.out("void, ")
|
||||
if t % 16 == 0:
|
||||
cog.outl("")
|
||||
cog.out(" ")
|
||||
cog.outl("void>")
|
||||
cog.outl(" : public imessage_router<void>")
|
||||
cog.outl("{")
|
||||
cog.outl("public:")
|
||||
cog.outl("")
|
||||
cog.outl(" void receive(imessage_router<void>& source, const imessage& msg)")
|
||||
cog.outl(" {")
|
||||
cog.outl(" const size_t id = msg.get_message_id();")
|
||||
cog.outl("")
|
||||
cog.outl(" switch (id)")
|
||||
cog.outl(" {")
|
||||
for t in range(1, n + 1):
|
||||
cog.out(" case T%d::ID:" % t)
|
||||
cog.out(" static_cast<TProcessor*>(this)->on_receive(source, static_cast<const T%d&>(msg));" % t)
|
||||
cog.outl(" break;")
|
||||
cog.out(" default:")
|
||||
cog.out(" static_cast<TProcessor*>(this)->on_receive_unknown(source, msg);")
|
||||
cog.outl(" break;")
|
||||
cog.outl(" }")
|
||||
cog.outl(" }")
|
||||
cog.outl("};")
|
||||
]]]*/
|
||||
/*[[[end]]]*/
|
||||
}
|
||||
|
||||
#endif
|
||||
497
test/test_fsm.cpp
Normal file
497
test/test_fsm.cpp
Normal file
@ -0,0 +1,497 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2017 jwellbelove
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#include "UnitTest++.h"
|
||||
|
||||
#include "fsm.h"
|
||||
#include "enum_type.h"
|
||||
#include "container.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace
|
||||
{
|
||||
//***************************************************************************
|
||||
// Events
|
||||
struct EventId
|
||||
{
|
||||
enum enum_type
|
||||
{
|
||||
START,
|
||||
STOP,
|
||||
STOPPED,
|
||||
SET_SPEED
|
||||
};
|
||||
|
||||
ETL_DECLARE_ENUM_TYPE(EventId, etl::fsm_event_id_t)
|
||||
ETL_ENUM_TYPE(START, "Start")
|
||||
ETL_ENUM_TYPE(STOP, "Stop")
|
||||
ETL_ENUM_TYPE(STOPPED, "Stopped")
|
||||
ETL_ENUM_TYPE(SET_SPEED, "Set Speed")
|
||||
ETL_END_ENUM_TYPE
|
||||
};
|
||||
|
||||
//***********************************
|
||||
class Start : public etl::fsm_event<EventId::START>
|
||||
{
|
||||
};
|
||||
|
||||
//***********************************
|
||||
class Stop : public etl::fsm_event<EventId::STOP>
|
||||
{
|
||||
public:
|
||||
|
||||
Stop() : isEmergencyStop(false) {}
|
||||
Stop(bool emergency) : isEmergencyStop(emergency) {}
|
||||
|
||||
const bool isEmergencyStop;
|
||||
};
|
||||
|
||||
//***********************************
|
||||
class SetSpeed : public etl::fsm_event<EventId::SET_SPEED>
|
||||
{
|
||||
public:
|
||||
|
||||
SetSpeed(int speed) : speed(speed) {}
|
||||
|
||||
const int speed;
|
||||
};
|
||||
|
||||
//***********************************
|
||||
class Stopped : public etl::fsm_event<EventId::STOPPED>
|
||||
{
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// States
|
||||
struct StateId
|
||||
{
|
||||
enum enum_type
|
||||
{
|
||||
IDLE,
|
||||
RUNNING,
|
||||
WINDING_DOWN,
|
||||
NUMBER_OF_STATES
|
||||
};
|
||||
|
||||
ETL_DECLARE_ENUM_TYPE(StateId, etl::fsm_state_id_t)
|
||||
ETL_ENUM_TYPE(IDLE, "Idle")
|
||||
ETL_ENUM_TYPE(RUNNING, "Running")
|
||||
ETL_ENUM_TYPE(WINDING_DOWN, "Winding Down")
|
||||
ETL_END_ENUM_TYPE
|
||||
};
|
||||
|
||||
class MotorControl;
|
||||
|
||||
//***********************************
|
||||
// Common functionality
|
||||
//***********************************
|
||||
class Common
|
||||
{
|
||||
public:
|
||||
|
||||
//***********************************
|
||||
Common()
|
||||
{
|
||||
ClearStatistics();
|
||||
}
|
||||
|
||||
//***********************************
|
||||
void ClearStatistics()
|
||||
{
|
||||
startCount = 0;
|
||||
stopCount = 0;
|
||||
setSpeedCount = 0;
|
||||
unknownCount = 0;
|
||||
stoppedCount = 0;
|
||||
isLampOn = false;
|
||||
speed = 0;
|
||||
}
|
||||
|
||||
//***********************************
|
||||
void SetSpeed(int speed_)
|
||||
{
|
||||
speed = speed_;
|
||||
}
|
||||
|
||||
//***********************************
|
||||
void TurnRunningLampOn()
|
||||
{
|
||||
isLampOn = true;
|
||||
}
|
||||
|
||||
//***********************************
|
||||
void TurnRunningLampOff()
|
||||
{
|
||||
isLampOn = false;
|
||||
}
|
||||
|
||||
int startCount;
|
||||
int stopCount;
|
||||
int setSpeedCount;
|
||||
int unknownCount;
|
||||
int stoppedCount;
|
||||
bool isLampOn;
|
||||
int speed;
|
||||
};
|
||||
|
||||
//***********************************
|
||||
// The idle state.
|
||||
//***********************************
|
||||
class Idle : public etl::fsm_state<Idle, StateId::IDLE, Start>
|
||||
{
|
||||
public:
|
||||
|
||||
//***********************************
|
||||
Idle(Common& common)
|
||||
: common(common)
|
||||
{
|
||||
}
|
||||
|
||||
//***********************************
|
||||
etl::fsm_state_id_t on_event(const Start& event)
|
||||
{
|
||||
++common.startCount;
|
||||
return StateId::RUNNING;
|
||||
}
|
||||
|
||||
//***********************************
|
||||
etl::fsm_state_id_t on_event_unknown(const etl::ifsm_event& event)
|
||||
{
|
||||
++common.unknownCount;
|
||||
return STATE_ID;
|
||||
}
|
||||
|
||||
//***********************************
|
||||
void on_enter_state()
|
||||
{
|
||||
common.TurnRunningLampOff();
|
||||
}
|
||||
|
||||
Common& common;
|
||||
};
|
||||
|
||||
//***********************************
|
||||
// The running state.
|
||||
//***********************************
|
||||
class Running : public etl::fsm_state<Running, StateId::RUNNING, Stop, SetSpeed>
|
||||
{
|
||||
public:
|
||||
|
||||
//***********************************
|
||||
Running(Common& common)
|
||||
: common(common)
|
||||
{
|
||||
}
|
||||
|
||||
//***********************************
|
||||
etl::fsm_state_id_t on_event(const Stop& event)
|
||||
{
|
||||
++common.stopCount;
|
||||
|
||||
if (event.isEmergencyStop)
|
||||
{
|
||||
return StateId::IDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return StateId::WINDING_DOWN;
|
||||
}
|
||||
}
|
||||
|
||||
//***********************************
|
||||
etl::fsm_state_id_t on_event(const SetSpeed& event)
|
||||
{
|
||||
++common.setSpeedCount;
|
||||
common.SetSpeed(event.speed);
|
||||
return STATE_ID;
|
||||
}
|
||||
|
||||
//***********************************
|
||||
etl::fsm_state_id_t on_event_unknown(const etl::ifsm_event& event)
|
||||
{
|
||||
++common.unknownCount;
|
||||
return STATE_ID;
|
||||
}
|
||||
|
||||
void on_enter_state()
|
||||
{
|
||||
common.TurnRunningLampOn();
|
||||
}
|
||||
|
||||
Common& common;
|
||||
};
|
||||
|
||||
//***********************************
|
||||
// The winding down state.
|
||||
//***********************************
|
||||
class WindingDown : public etl::fsm_state<WindingDown, StateId::WINDING_DOWN, Stopped>
|
||||
{
|
||||
public:
|
||||
|
||||
//***********************************
|
||||
WindingDown(Common& common)
|
||||
: common(common)
|
||||
{
|
||||
}
|
||||
|
||||
//***********************************
|
||||
etl::fsm_state_id_t on_event(const Stopped& event)
|
||||
{
|
||||
++common.stoppedCount;
|
||||
return StateId::IDLE;
|
||||
}
|
||||
|
||||
//***********************************
|
||||
etl::fsm_state_id_t on_event_unknown(const etl::ifsm_event& event)
|
||||
{
|
||||
++common.unknownCount;
|
||||
return STATE_ID;
|
||||
}
|
||||
|
||||
Common& common;
|
||||
};
|
||||
|
||||
//***********************************
|
||||
// The motor control FSM.
|
||||
//***********************************
|
||||
class MotorControl : public etl::fsm
|
||||
{
|
||||
public:
|
||||
|
||||
MotorControl()
|
||||
: idle(common),
|
||||
running(common),
|
||||
windingDown(common)
|
||||
{
|
||||
set_states(stateList, etl::size(stateList));
|
||||
}
|
||||
|
||||
Common common;
|
||||
|
||||
private:
|
||||
|
||||
Idle idle;
|
||||
Running running;
|
||||
WindingDown windingDown;
|
||||
|
||||
etl::ifsm_state* stateList[StateId::NUMBER_OF_STATES] =
|
||||
{
|
||||
&idle, &running, &windingDown
|
||||
};
|
||||
};
|
||||
|
||||
MotorControl motorControl;
|
||||
|
||||
SUITE(test_map)
|
||||
{
|
||||
//*************************************************************************
|
||||
TEST(test_fsm)
|
||||
{
|
||||
motorControl.reset();
|
||||
motorControl.common.ClearStatistics();
|
||||
|
||||
CHECK(!motorControl.is_started());
|
||||
|
||||
// Start the FSM.
|
||||
motorControl.start();
|
||||
CHECK(motorControl.is_started());
|
||||
|
||||
// Now in Idle state.
|
||||
|
||||
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
CHECK_EQUAL(false, motorControl.common.isLampOn);
|
||||
CHECK_EQUAL(0, motorControl.common.setSpeedCount);
|
||||
CHECK_EQUAL(0, motorControl.common.speed);
|
||||
CHECK_EQUAL(0, motorControl.common.startCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stopCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stoppedCount);
|
||||
CHECK_EQUAL(0, motorControl.common.unknownCount);
|
||||
|
||||
// Send unhandled events.
|
||||
motorControl.on_event(Stop());
|
||||
motorControl.on_event(Stopped());
|
||||
motorControl.on_event(SetSpeed(10));
|
||||
|
||||
CHECK_EQUAL(StateId::IDLE, motorControl.get_state_id());
|
||||
CHECK_EQUAL(StateId::IDLE, motorControl.get_state().get_state_id());
|
||||
|
||||
CHECK_EQUAL(false, motorControl.common.isLampOn);
|
||||
CHECK_EQUAL(0, motorControl.common.setSpeedCount);
|
||||
CHECK_EQUAL(0, motorControl.common.speed);
|
||||
CHECK_EQUAL(0, motorControl.common.startCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stopCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stoppedCount);
|
||||
CHECK_EQUAL(3, motorControl.common.unknownCount);
|
||||
|
||||
// Send Start event.
|
||||
motorControl.on_event(Start());
|
||||
|
||||
// Now in Running state.
|
||||
|
||||
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
CHECK_EQUAL(true, motorControl.common.isLampOn);
|
||||
CHECK_EQUAL(0, motorControl.common.setSpeedCount);
|
||||
CHECK_EQUAL(0, motorControl.common.speed);
|
||||
CHECK_EQUAL(1, motorControl.common.startCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stopCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stoppedCount);
|
||||
CHECK_EQUAL(3, motorControl.common.unknownCount);
|
||||
|
||||
// Send unhandled events.
|
||||
motorControl.on_event(Start());
|
||||
motorControl.on_event(Stopped());
|
||||
|
||||
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
CHECK_EQUAL(true, motorControl.common.isLampOn);
|
||||
CHECK_EQUAL(0, motorControl.common.setSpeedCount);
|
||||
CHECK_EQUAL(0, motorControl.common.speed);
|
||||
CHECK_EQUAL(1, motorControl.common.startCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stopCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stoppedCount);
|
||||
CHECK_EQUAL(5, motorControl.common.unknownCount);
|
||||
|
||||
// Send SetSpeed event.
|
||||
motorControl.on_event(SetSpeed(100));
|
||||
|
||||
// Still in Running state.
|
||||
|
||||
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
CHECK_EQUAL(true, motorControl.common.isLampOn);
|
||||
CHECK_EQUAL(1, motorControl.common.setSpeedCount);
|
||||
CHECK_EQUAL(100, motorControl.common.speed);
|
||||
CHECK_EQUAL(1, motorControl.common.startCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stopCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stoppedCount);
|
||||
CHECK_EQUAL(5, motorControl.common.unknownCount);
|
||||
|
||||
// Send Stop event.
|
||||
motorControl.on_event(Stop());
|
||||
|
||||
// Now in WindingDown state.
|
||||
|
||||
CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
CHECK_EQUAL(true, motorControl.common.isLampOn);
|
||||
CHECK_EQUAL(1, motorControl.common.setSpeedCount);
|
||||
CHECK_EQUAL(100, motorControl.common.speed);
|
||||
CHECK_EQUAL(1, motorControl.common.startCount);
|
||||
CHECK_EQUAL(1, motorControl.common.stopCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stoppedCount);
|
||||
CHECK_EQUAL(5, motorControl.common.unknownCount);
|
||||
|
||||
// Send unhandled events.
|
||||
motorControl.on_event(Start());
|
||||
motorControl.on_event(Stop());
|
||||
motorControl.on_event(SetSpeed(100));
|
||||
|
||||
CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::WINDING_DOWN, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
CHECK_EQUAL(true, motorControl.common.isLampOn);
|
||||
CHECK_EQUAL(1, motorControl.common.setSpeedCount);
|
||||
CHECK_EQUAL(100, motorControl.common.speed);
|
||||
CHECK_EQUAL(1, motorControl.common.startCount);
|
||||
CHECK_EQUAL(1, motorControl.common.stopCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stoppedCount);
|
||||
CHECK_EQUAL(8, motorControl.common.unknownCount);
|
||||
|
||||
// Send Stopped event.
|
||||
motorControl.on_event(Stopped());
|
||||
|
||||
// Now in Idle state.
|
||||
|
||||
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
CHECK_EQUAL(false, motorControl.common.isLampOn);
|
||||
CHECK_EQUAL(1, motorControl.common.setSpeedCount);
|
||||
CHECK_EQUAL(100, motorControl.common.speed);
|
||||
CHECK_EQUAL(1, motorControl.common.startCount);
|
||||
CHECK_EQUAL(1, motorControl.common.stopCount);
|
||||
CHECK_EQUAL(1, motorControl.common.stoppedCount);
|
||||
CHECK_EQUAL(8, motorControl.common.unknownCount);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_fsm_emergency_stop)
|
||||
{
|
||||
motorControl.reset();
|
||||
motorControl.common.ClearStatistics();
|
||||
|
||||
CHECK(!motorControl.is_started());
|
||||
|
||||
// Start the FSM.
|
||||
motorControl.start();
|
||||
CHECK(motorControl.is_started());
|
||||
|
||||
// Now in Idle state.
|
||||
|
||||
// Send Start event.
|
||||
motorControl.on_event(Start());
|
||||
|
||||
// Now in Running state.
|
||||
|
||||
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::RUNNING, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
CHECK_EQUAL(true, motorControl.common.isLampOn);
|
||||
CHECK_EQUAL(0, motorControl.common.setSpeedCount);
|
||||
CHECK_EQUAL(0, motorControl.common.speed);
|
||||
CHECK_EQUAL(1, motorControl.common.startCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stopCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stoppedCount);
|
||||
CHECK_EQUAL(0, motorControl.common.unknownCount);
|
||||
|
||||
// Send emergency Stop event.
|
||||
motorControl.on_event(Stop(true));
|
||||
|
||||
// Now in Idle state.
|
||||
|
||||
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state_id()));
|
||||
CHECK_EQUAL(StateId::IDLE, int(motorControl.get_state().get_state_id()));
|
||||
|
||||
CHECK_EQUAL(false, motorControl.common.isLampOn);
|
||||
CHECK_EQUAL(0, motorControl.common.setSpeedCount);
|
||||
CHECK_EQUAL(0, motorControl.common.speed);
|
||||
CHECK_EQUAL(1, motorControl.common.startCount);
|
||||
CHECK_EQUAL(1, motorControl.common.stopCount);
|
||||
CHECK_EQUAL(0, motorControl.common.stoppedCount);
|
||||
CHECK_EQUAL(0, motorControl.common.unknownCount);
|
||||
}
|
||||
};
|
||||
}
|
||||
528
test/test_message_router.cpp
Normal file
528
test/test_message_router.cpp
Normal file
@ -0,0 +1,528 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
http://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2017 jwellbelove
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files(the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions :
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
#include "UnitTest++.h"
|
||||
#include "ExtraCheckMacros.h"
|
||||
|
||||
#include "message_router.h"
|
||||
|
||||
//***************************************************************************
|
||||
// The set of messages.
|
||||
//***************************************************************************
|
||||
namespace
|
||||
{
|
||||
enum
|
||||
{
|
||||
MESSAGE1,
|
||||
MESSAGE2,
|
||||
MESSAGE3,
|
||||
MESSAGE4,
|
||||
MESSAGE5
|
||||
};
|
||||
|
||||
struct Message1 : public etl::message<MESSAGE1>
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
struct Message2 : public etl::message<MESSAGE2>
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
struct Message3 : public etl::message<MESSAGE3>
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
struct Message4 : public etl::message<MESSAGE4>
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
struct Message5 : public etl::message<MESSAGE5>
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
Message1 message1;
|
||||
Message2 message2;
|
||||
Message3 message3;
|
||||
Message4 message4;
|
||||
Message5 message5;
|
||||
|
||||
// Router typedefs.
|
||||
typedef etl::imessage_router<int> int_router_t;
|
||||
typedef etl::imessage_router<void> void_router_t;
|
||||
|
||||
//***************************************************************************
|
||||
// Router that handles messages 1, 2, 3, 4 and 5 and returns nothing.
|
||||
//***************************************************************************
|
||||
class RouterVoid1 : public etl::message_router<RouterVoid1, void, Message1, Message2, Message3, Message4, Message5>
|
||||
{
|
||||
public:
|
||||
|
||||
RouterVoid1()
|
||||
: message1_count(0),
|
||||
message2_count(0),
|
||||
message3_count(0),
|
||||
message4_count(0),
|
||||
message_unknown_count(0),
|
||||
callback_count(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void on_receive(void_router_t& sender, const Message1& msg)
|
||||
{
|
||||
++message1_count;
|
||||
send_to(sender, message5);
|
||||
}
|
||||
|
||||
void on_receive(void_router_t& sender, const Message2& msg)
|
||||
{
|
||||
++message2_count;
|
||||
send_to(sender, message5);
|
||||
}
|
||||
|
||||
void on_receive(void_router_t& sender, const Message3& msg)
|
||||
{
|
||||
++message3_count;
|
||||
send_to(sender, message5);
|
||||
}
|
||||
|
||||
void on_receive(void_router_t& sender, const Message4& msg)
|
||||
{
|
||||
++message4_count;
|
||||
send_to(sender, message5);
|
||||
}
|
||||
|
||||
void on_receive(void_router_t& sender, const Message5& msg)
|
||||
{
|
||||
++callback_count;
|
||||
}
|
||||
|
||||
void on_receive_unknown(void_router_t& sender, const etl::imessage& msg)
|
||||
{
|
||||
++message_unknown_count;
|
||||
}
|
||||
|
||||
int message1_count;
|
||||
int message2_count;
|
||||
int message3_count;
|
||||
int message4_count;
|
||||
int message_unknown_count;
|
||||
int callback_count;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// Router that handles messages 1, 2, 4 and 5 and returns nothing.
|
||||
//***************************************************************************
|
||||
class RouterVoid2 : public etl::message_router<RouterVoid2, void, Message1, Message2, Message4, Message5>
|
||||
{
|
||||
public:
|
||||
|
||||
RouterVoid2()
|
||||
: message1_count(0),
|
||||
message2_count(0),
|
||||
message4_count(0),
|
||||
message_unknown_count(0),
|
||||
callback_count(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void on_receive(void_router_t& sender, const Message1& msg)
|
||||
{
|
||||
++message1_count;
|
||||
send_to(sender, message5);
|
||||
}
|
||||
|
||||
void on_receive(void_router_t& sender, const Message2& msg)
|
||||
{
|
||||
++message2_count;
|
||||
send_to(sender, message5);
|
||||
}
|
||||
|
||||
void on_receive(void_router_t& sender, const Message4& msg)
|
||||
{
|
||||
++message4_count;
|
||||
send_to(sender, message5);
|
||||
}
|
||||
|
||||
void on_receive(void_router_t& sender, const Message5& msg)
|
||||
{
|
||||
++callback_count;
|
||||
}
|
||||
|
||||
void on_receive_unknown(void_router_t& sender, const etl::imessage& msg)
|
||||
{
|
||||
++message_unknown_count;
|
||||
send_to(sender, message5);
|
||||
}
|
||||
|
||||
int message1_count;
|
||||
int message2_count;
|
||||
int message4_count;
|
||||
int message_unknown_count;
|
||||
int callback_count;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// Router that handles messages 1, 2, 3, 4 and 5 and returns an int.
|
||||
//***************************************************************************
|
||||
class RouterInt1 : public etl::message_router<RouterInt1, int, Message1, Message2, Message3, Message4, Message5>
|
||||
{
|
||||
public:
|
||||
|
||||
RouterInt1()
|
||||
: message1_count(0),
|
||||
message2_count(0),
|
||||
message3_count(0),
|
||||
message4_count(0),
|
||||
message_unknown_count(0),
|
||||
callback_count(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int on_receive(int_router_t& sender, const Message1& msg)
|
||||
{
|
||||
++message1_count;
|
||||
send_to(sender, message5);
|
||||
return msg.ID;
|
||||
}
|
||||
|
||||
int on_receive(int_router_t& sender, const Message2& msg)
|
||||
{
|
||||
++message2_count;
|
||||
send_to(sender, message5);
|
||||
return msg.ID;
|
||||
}
|
||||
|
||||
int on_receive(int_router_t& sender, const Message3& msg)
|
||||
{
|
||||
++message3_count;
|
||||
send_to(sender, message5);
|
||||
return msg.ID;
|
||||
}
|
||||
|
||||
int on_receive(int_router_t& sender, const Message4& msg)
|
||||
{
|
||||
++message4_count;
|
||||
send_to(sender, message5);
|
||||
return msg.ID;
|
||||
}
|
||||
|
||||
int on_receive(int_router_t& sender, const Message5& msg)
|
||||
{
|
||||
++callback_count;
|
||||
return msg.ID;
|
||||
}
|
||||
|
||||
int on_receive_unknown(int_router_t& sender, const etl::imessage& msg)
|
||||
{
|
||||
++message_unknown_count;
|
||||
return msg.get_message_id();
|
||||
}
|
||||
|
||||
int message1_count;
|
||||
int message2_count;
|
||||
int message3_count;
|
||||
int message4_count;
|
||||
int message_unknown_count;
|
||||
int callback_count;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// Router that handles messages 1, 2, 4 and 5 and returns an int.
|
||||
//***************************************************************************
|
||||
class RouterInt2 : public etl::message_router<RouterInt2, int, Message1, Message2, Message4, Message5>
|
||||
{
|
||||
public:
|
||||
|
||||
RouterInt2()
|
||||
: message1_count(0),
|
||||
message2_count(0),
|
||||
message4_count(0),
|
||||
message_unknown_count(0),
|
||||
callback_count(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int on_receive(int_router_t& sender, const Message1& msg)
|
||||
{
|
||||
++message1_count;
|
||||
send_to(sender, message5);
|
||||
return msg.ID;
|
||||
}
|
||||
|
||||
int on_receive(int_router_t& sender, const Message2& msg)
|
||||
{
|
||||
++message2_count;
|
||||
send_to(sender, message5);
|
||||
return msg.ID;
|
||||
}
|
||||
|
||||
int on_receive(int_router_t& sender, const Message4& msg)
|
||||
{
|
||||
++message4_count;
|
||||
send_to(sender, message5);
|
||||
return msg.ID;
|
||||
}
|
||||
|
||||
int on_receive(int_router_t& sender, const Message5& msg)
|
||||
{
|
||||
++callback_count;
|
||||
return msg.ID;
|
||||
}
|
||||
|
||||
int on_receive_unknown(int_router_t& sender, const etl::imessage& msg)
|
||||
{
|
||||
++message_unknown_count;
|
||||
send_to(sender, message5);
|
||||
return msg.get_message_id();
|
||||
}
|
||||
|
||||
int message1_count;
|
||||
int message2_count;
|
||||
int message4_count;
|
||||
int message_unknown_count;
|
||||
int callback_count;
|
||||
};
|
||||
|
||||
// Null router types.
|
||||
typedef etl::null_message_router<int> int_null_router_t;
|
||||
typedef etl::null_message_router<void> void_null_router_t;
|
||||
|
||||
void_router_t* p_void_router;
|
||||
int_router_t* p_int_router;
|
||||
|
||||
SUITE(test_message_router)
|
||||
{
|
||||
//=========================================================================
|
||||
TEST(message_router_void)
|
||||
{
|
||||
RouterVoid1 r1;
|
||||
RouterVoid2 r2;
|
||||
|
||||
p_void_router = &r1;
|
||||
|
||||
p_void_router->receive(r2, message1);
|
||||
CHECK_EQUAL(1, r1.message1_count);
|
||||
CHECK_EQUAL(0, r1.message2_count);
|
||||
CHECK_EQUAL(0, r1.message3_count);
|
||||
CHECK_EQUAL(0, r1.message4_count);
|
||||
CHECK_EQUAL(0, r1.message_unknown_count);
|
||||
CHECK_EQUAL(1, r2.callback_count);
|
||||
|
||||
p_void_router->receive(r2, message2);
|
||||
CHECK_EQUAL(1, r1.message1_count);
|
||||
CHECK_EQUAL(1, r1.message2_count);
|
||||
CHECK_EQUAL(0, r1.message3_count);
|
||||
CHECK_EQUAL(0, r1.message4_count);
|
||||
CHECK_EQUAL(0, r1.message_unknown_count);
|
||||
CHECK_EQUAL(2, r2.callback_count);
|
||||
|
||||
p_void_router->receive(r2, message3);
|
||||
CHECK_EQUAL(1, r1.message1_count);
|
||||
CHECK_EQUAL(1, r1.message2_count);
|
||||
CHECK_EQUAL(1, r1.message3_count);
|
||||
CHECK_EQUAL(0, r1.message4_count);
|
||||
CHECK_EQUAL(0, r1.message_unknown_count);
|
||||
CHECK_EQUAL(3, r2.callback_count);
|
||||
|
||||
p_void_router->receive(r2, message4);
|
||||
CHECK_EQUAL(1, r1.message1_count);
|
||||
CHECK_EQUAL(1, r1.message2_count);
|
||||
CHECK_EQUAL(1, r1.message3_count);
|
||||
CHECK_EQUAL(1, r1.message4_count);
|
||||
CHECK_EQUAL(0, r1.message_unknown_count);
|
||||
CHECK_EQUAL(4, r2.callback_count);
|
||||
|
||||
p_void_router = &r2;
|
||||
|
||||
p_void_router->receive(r1, message1);
|
||||
CHECK_EQUAL(1, r2.message1_count);
|
||||
CHECK_EQUAL(0, r2.message2_count);
|
||||
CHECK_EQUAL(0, r2.message4_count);
|
||||
CHECK_EQUAL(0, r2.message_unknown_count);
|
||||
CHECK_EQUAL(1, r1.callback_count);
|
||||
|
||||
p_void_router->receive(r1, message2);
|
||||
CHECK_EQUAL(1, r2.message1_count);
|
||||
CHECK_EQUAL(1, r2.message2_count);
|
||||
CHECK_EQUAL(0, r2.message4_count);
|
||||
CHECK_EQUAL(0, r2.message_unknown_count);
|
||||
CHECK_EQUAL(2, r1.callback_count);
|
||||
|
||||
p_void_router->receive(r1, message3);
|
||||
CHECK_EQUAL(1, r2.message1_count);
|
||||
CHECK_EQUAL(1, r2.message2_count);
|
||||
CHECK_EQUAL(0, r2.message4_count);
|
||||
CHECK_EQUAL(1, r2.message_unknown_count);
|
||||
CHECK_EQUAL(3, r1.callback_count);
|
||||
|
||||
p_void_router->receive(r1, message4);
|
||||
CHECK_EQUAL(1, r2.message1_count);
|
||||
CHECK_EQUAL(1, r2.message2_count);
|
||||
CHECK_EQUAL(1, r2.message4_count);
|
||||
CHECK_EQUAL(1, r2.message_unknown_count);
|
||||
CHECK_EQUAL(4, r1.callback_count);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
TEST(message_router_int)
|
||||
{
|
||||
RouterInt1 r1;
|
||||
RouterInt2 r2;
|
||||
|
||||
p_int_router = &r1;
|
||||
|
||||
CHECK_EQUAL(int(message1.ID), (p_int_router->receive(r2, message1)));
|
||||
CHECK_EQUAL(1, r1.message1_count);
|
||||
CHECK_EQUAL(0, r1.message2_count);
|
||||
CHECK_EQUAL(0, r1.message3_count);
|
||||
CHECK_EQUAL(0, r1.message4_count);
|
||||
CHECK_EQUAL(0, r1.message_unknown_count);
|
||||
CHECK_EQUAL(1, r2.callback_count);
|
||||
|
||||
CHECK_EQUAL(int(message2.ID), (p_int_router->receive(r2, message2)));
|
||||
CHECK_EQUAL(1, r1.message1_count);
|
||||
CHECK_EQUAL(1, r1.message2_count);
|
||||
CHECK_EQUAL(0, r1.message3_count);
|
||||
CHECK_EQUAL(0, r1.message4_count);
|
||||
CHECK_EQUAL(0, r1.message_unknown_count);
|
||||
CHECK_EQUAL(2, r2.callback_count);
|
||||
|
||||
CHECK_EQUAL(int(message3.ID), (p_int_router->receive(r2, message3)));
|
||||
CHECK_EQUAL(1, r1.message1_count);
|
||||
CHECK_EQUAL(1, r1.message2_count);
|
||||
CHECK_EQUAL(1, r1.message3_count);
|
||||
CHECK_EQUAL(0, r1.message4_count);
|
||||
CHECK_EQUAL(0, r1.message_unknown_count);
|
||||
CHECK_EQUAL(3, r2.callback_count);
|
||||
|
||||
CHECK_EQUAL(int(message4.ID), (p_int_router->receive(r2, message4)));
|
||||
CHECK_EQUAL(1, r1.message1_count);
|
||||
CHECK_EQUAL(1, r1.message2_count);
|
||||
CHECK_EQUAL(1, r1.message3_count);
|
||||
CHECK_EQUAL(1, r1.message4_count);
|
||||
CHECK_EQUAL(0, r1.message_unknown_count);
|
||||
CHECK_EQUAL(4, r2.callback_count);
|
||||
|
||||
p_int_router = &r2;
|
||||
|
||||
CHECK_EQUAL(int(message1.ID), (p_int_router->receive(r1, message1)));
|
||||
CHECK_EQUAL(1, r2.message1_count);
|
||||
CHECK_EQUAL(0, r2.message2_count);
|
||||
CHECK_EQUAL(0, r2.message4_count);
|
||||
CHECK_EQUAL(0, r2.message_unknown_count);
|
||||
CHECK_EQUAL(1, r1.callback_count);
|
||||
|
||||
CHECK_EQUAL(int(message2.ID), (p_int_router->receive(r1, message2)));
|
||||
CHECK_EQUAL(1, r2.message1_count);
|
||||
CHECK_EQUAL(1, r2.message2_count);
|
||||
CHECK_EQUAL(0, r2.message4_count);
|
||||
CHECK_EQUAL(0, r2.message_unknown_count);
|
||||
CHECK_EQUAL(2, r1.callback_count);
|
||||
|
||||
CHECK_EQUAL(int(message3.ID), (p_int_router->receive(r1, message3)));
|
||||
CHECK_EQUAL(1, r2.message1_count);
|
||||
CHECK_EQUAL(1, r2.message2_count);
|
||||
CHECK_EQUAL(0, r2.message4_count);
|
||||
CHECK_EQUAL(1, r2.message_unknown_count);
|
||||
CHECK_EQUAL(3, r1.callback_count);
|
||||
|
||||
CHECK_EQUAL(int(message4.ID), (p_int_router->receive(r1, message4)));
|
||||
CHECK_EQUAL(1, r2.message1_count);
|
||||
CHECK_EQUAL(1, r2.message2_count);
|
||||
CHECK_EQUAL(1, r2.message4_count);
|
||||
CHECK_EQUAL(1, r2.message_unknown_count);
|
||||
CHECK_EQUAL(4, r1.callback_count);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
TEST(message_null_router_void)
|
||||
{
|
||||
RouterVoid2 router;
|
||||
void_null_router_t null_router;
|
||||
|
||||
// Send from the null router.
|
||||
null_router.send_to(router, message1);
|
||||
CHECK_EQUAL(1, router.message1_count);
|
||||
CHECK_EQUAL(0, router.message2_count);
|
||||
CHECK_EQUAL(0, router.message4_count);
|
||||
CHECK_EQUAL(0, router.message_unknown_count);
|
||||
|
||||
null_router.send_to(router, message2);
|
||||
CHECK_EQUAL(1, router.message1_count);
|
||||
CHECK_EQUAL(1, router.message2_count);
|
||||
CHECK_EQUAL(0, router.message4_count);
|
||||
CHECK_EQUAL(0, router.message_unknown_count);
|
||||
|
||||
null_router.send_to(router, message3);
|
||||
CHECK_EQUAL(1, router.message1_count);
|
||||
CHECK_EQUAL(1, router.message2_count);
|
||||
CHECK_EQUAL(0, router.message4_count);
|
||||
CHECK_EQUAL(1, router.message_unknown_count);
|
||||
|
||||
null_router.send_to(router, message4);
|
||||
CHECK_EQUAL(1, router.message1_count);
|
||||
CHECK_EQUAL(1, router.message2_count);
|
||||
CHECK_EQUAL(1, router.message4_count);
|
||||
CHECK_EQUAL(1, router.message_unknown_count);
|
||||
|
||||
// Send to the null router.
|
||||
router.send_to(null_router, message1);
|
||||
CHECK_EQUAL(1, router.message1_count);
|
||||
CHECK_EQUAL(1, router.message2_count);
|
||||
CHECK_EQUAL(1, router.message4_count);
|
||||
CHECK_EQUAL(1, router.message_unknown_count);
|
||||
|
||||
router.send_to(null_router, message2);
|
||||
CHECK_EQUAL(1, router.message1_count);
|
||||
CHECK_EQUAL(1, router.message2_count);
|
||||
CHECK_EQUAL(1, router.message4_count);
|
||||
CHECK_EQUAL(1, router.message_unknown_count);
|
||||
|
||||
router.send_to(null_router, message3);
|
||||
CHECK_EQUAL(1, router.message1_count);
|
||||
CHECK_EQUAL(1, router.message2_count);
|
||||
CHECK_EQUAL(1, router.message4_count);
|
||||
CHECK_EQUAL(1, router.message_unknown_count);
|
||||
|
||||
router.send_to(null_router, message4);
|
||||
CHECK_EQUAL(1, router.message1_count);
|
||||
CHECK_EQUAL(1, router.message2_count);
|
||||
CHECK_EQUAL(1, router.message4_count);
|
||||
CHECK_EQUAL(1, router.message_unknown_count);
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user