I have this working code for C++ state machine using STL (not using boost state chart).
The purpose of posting is two-fold:
To share a simple and working code C++ state machine using the STL for the sparse 'state' and 'event' function matrix.
To seek enhancements of implementation, since with this approach I have to init state machine for every new object created, which is not necessary since state-event function are same for all objects of a given class.
(Note: while compiling using g++, use the "-std=c++0x" option and link both .cpp files to get the executable)
File name: BasicFsmT.h
//////////////////////////////////////////////////////////////////////
// File name : BasicFsmT.h
// Purpose : To define simple FSM implementation base class.
// This class to be inherited
// by any class which would like to implement FSM
///////////////////////////////////////////////////////////////////////
#ifndef _BasicFsmT_h
#define _BasicFsmT_h
#include <string>
#include <map>
#include <iostream>
#include <vector>
using namespace std;
class BasicFsm
{
public :
BasicFsm(); // Default Constructor
BasicFsm(string initialState);
virtual ~BasicFsm();
// StateMachine Method prototype
typedef int (*pFsmMethodT) (BasicFsm* pFSM, string event, int info);
virtual int AddState(string state);
virtual int AddStateEventMethod(string state, string event, pFsmMethodT);
virtual string GetState();
virtual int Fsm(string event, int info);
virtual int TransitionToState(string state);
virtual int SetPreviousEvent(string event);
protected :
vector<string> m_statesVec; // all valid states list in the fsm
vector<string> m_eventsVec; // all valid events list in the fsm
string m_fsmStateStr; // current state of the state fsm
string m_fsmPrevEventStr; // the last event procesed by the fsm
string m_fsmPrevStateStr; // the previous state of fsm
// the key is state and event strings concatanated together
map<string, pFsmMethodT> m_stateEventToMethodMap;
};
#endif // _BasicFsmT_h
////////////// End File : BasicFsmT.h ///////////////////////////////
File name: BasicFsmT.cpp
//////////////////////////////////////////////////////////////////////
// File name : BasicFsmT.cpp
// Purpose : To define BasicFsmT Class methods. This class to be inherited
// by any class which would like to implement FSM
///////////////////////////////////////////////////////////////////////
#include <BasicFsmT.h>
#include <algorithm> // for std::find( ) etc.
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <stdlib.h>
/// Description : Default Constructor, called by derrived classes object
/// @param[in]
/// @returns
BasicFsm::BasicFsm()
{
m_statesVec.clear();
m_eventsVec.clear();
m_fsmStateStr.clear();
m_fsmPrevEventStr.clear();
m_fsmPrevStateStr.clear();
m_stateEventToMethodMap.clear();
}
/// Description : Constructor, with initial state to setup.
/// Need expicit call by derrived classes constructor.
/// @param[in]
/// @returns
BasicFsm::BasicFsm(string initialState)
{
m_statesVec.clear();
m_eventsVec.clear();
m_fsmStateStr.clear();
m_fsmPrevEventStr.clear();
m_fsmPrevStateStr.clear();
m_stateEventToMethodMap.clear();
m_statesVec.push_back(initialState);
m_fsmStateStr = initialState;
}
/// Description : Destructor, this can be overrided by derrived class's destructor
/// No special cleanup is needed..
/// @param[in]
/// @returns
BasicFsm::~BasicFsm()
{
}
/// Description : Add a state to be handled by the fsm
///
/// @param[in] : state to be handled by the fsm
/// @returns
int BasicFsm::AddState(string state)
{
vector<string>::iterator itr;
itr = find(m_statesVec.begin(), m_statesVec.end(), state);
if (itr == m_statesVec.end())
{
m_statesVec.push_back(state);
}
// else state is already available
}
/// Description : Store the address of Method/function to be called
/// for the specified state and event
/// @param[in] : state - at which the specified Method is applicable.
/// @param[in] : event - for which the specified Method is applicable.
/// @param[in] : pFunc - Address of the Method to be called for the
/// specified state and event.
/// @returns
int BasicFsm::AddStateEventMethod(string state, string event, pFsmMethodT pFunc)
{
string stateNEvent;
vector<string>::iterator itr;
itr = find(m_statesVec.begin(), m_statesVec.end(), state);
if (itr == m_statesVec.end())
{
m_statesVec.push_back(state);
}
// else state is already available
itr = find(m_eventsVec.begin(), m_eventsVec.end(), state);
if (itr == m_eventsVec.end())
{
m_eventsVec.push_back(event);
}
// else state is already available
// Add the function pointer to be called for the specified state-event pair
stateNEvent = state + event;
m_stateEventToMethodMap[stateNEvent] = pFunc;
}
/// Description : Get the current state of the fsm
/// @param[in] :
/// @returns
string BasicFsm::GetState()
{
return (m_fsmStateStr);
}
/// Description : Change fsm state to the specified state.
/// @param[in]
/// @returns
int BasicFsm::TransitionToState(string state)
{
vector<string>::iterator itr;
itr = find(m_statesVec.begin(), m_statesVec.end(), state);
if (itr != m_statesVec.end())
{ // transition to state is a valid state
m_fsmPrevStateStr = m_fsmStateStr;
m_fsmStateStr = state;
}
// else transition to state is not valid
}
/// Description : set fsm processed event to the specified event.
/// @param[in]
/// @returns
int BasicFsm::SetPreviousEvent(string event)
{
vector<string>::iterator itr;
itr = find(m_eventsVec.begin(), m_eventsVec.end(), event);
if (itr != m_eventsVec.end())
{ // transition to state is a valid state
m_fsmPrevEventStr = event;
}
// else event is not valid
}
/// Description : Calls the Method specified for the specified state
/// event
/// @param[in] event - The event to be processed
/// @param[in] info - Additional info needed for the method to process.
/// @returns -1 - If specfied state or event is in valid, or
/// no fsm function is defined for that state+event pair.
int BasicFsm::Fsm(string event, int info)
{
pFsmMethodT pFunc;
int retStatus;
string state, stateNEvent;
try {
state = GetState();
stateNEvent = state + event;
map<string, pFsmMethodT>::iterator itr =
m_stateEventToMethodMap.find(stateNEvent);
// validate state, event and event handler
if (itr == m_stateEventToMethodMap.end())
{
return(-1);
}
pFunc = itr->second; // get pointer to the function to be executed.
// retStatus = (this->*(pFunc))(this, event, info);
retStatus = (*(pFunc))(this, event, info);
SetPreviousEvent(event);
return(retStatus);
} // end try
catch(exception& Error) {
stringstream logErr;
logErr << "Exception at BasicFsm::fsm, Error : " << Error.what();
}
}
/////////// End File : BasicFsmT.cpp ////////////////////////////////
File name: BasicFsmTest.cpp
//////////////////////////////////////////////////////////////////////
// File name : BasicFsmTest.cpp
// Purpose : To test implementation of FSM by BasicFsmT Class.
// This includes BasicFsmT as public base class to implement a
// sample Telephone state machine.
//
///////////////////////////////////////////////////////////////////////
#include <BasicFsmT.h>
#include <algorithm> // for std::find( ) etc.
#include <iostream>
#include <sstream>
#include <stdexcept>
///////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////
/////// Test Functions for BasicFsm Class ////////////////////
namespace TelephoneEnumsT
{
// List of Valid Sate in the Stmc
const string S_Idle = "S_Idle";
const string S_DIGIT_COLLECTION = "S_DigitCollection";
const string S_ESTABLISHING = "S_Establishing";
const string S_RINGING_OG = "S_RingingOG";
const string S_RINGING_IC = "S_RingingIC";
const string S_CALL_ESTABLISHED = "S_Established";
const string S_CALL_FE_TERMINATED = "S_FETerminated";
// List of Valid Events in the Stmc
const string E_ONHOOK = "E_Onhook";
const string E_OFFOOK = "E_Offhook";
const string E_DIGIT_COLLECT = "E_DigitCollect";
const string E_DIGIT_COLLECT_COMPLETE = "E_DigitCollectComplete";
const string E_OG_RING = "E_OGRing";
const string E_REMOTE_ANSWER = "E_RemoteAnswer";
const string E_IC_RING = "E_ICRing";
const string E_TIMEOUT = "E_Timeout";
}; // end namespace declaration
class Telephone : public BasicFsm
{
private :
string digitsCollected;
string myNumber;
time_t call_st_time;
time_t call_end_time;
public :
Telephone();
Telephone(string myNumber);
~Telephone();
void DispDigitsCollected()
{
cout << "Digits collected so far = " << digitsCollected << endl;
}
friend int OffHookAtIdle(BasicFsm* pFsm, string event, int info);
friend int DigitCollect(BasicFsm* pFsm, string event, int info);
friend int DigitCollectComplete(BasicFsm* pFsm, string event, int info);
friend int TimeoutAtDigitCollection(BasicFsm* pFsm, string event, int info);
friend int OffHookAtEstablished(BasicFsm* pFsm, string event, int info);
/// This state-event processing list of functions are not complete
void InitFsm(); // This will init the fsm functions
}; // end of Telephone class declaration
using namespace std;
using namespace TelephoneEnumsT;
Telephone::Telephone()
{
digitsCollected.clear();
myNumber.clear();
call_st_time = 0;
call_end_time = 0;
}
Telephone::Telephone(string myNumber)
{
digitsCollected.clear();
myNumber.assign(myNumber);
call_st_time = 0;
call_end_time = 0;
}
Telephone::~Telephone()
{
}
int OffHookAtIdle(BasicFsm* pFsm, string event, int info)
{
stringstream logMsg;
try {
Telephone* pPhone = static_cast <Telephone*> (pFsm);
logMsg << "In Function OffHookAtIdle() " << " At State "
<< pFsm->GetState() << " For Event " << event << endl;
cout << logMsg.str() << endl;
// Do the required Processing
pPhone->digitsCollected.clear();
// Do state transition and do some book keeping
pPhone->TransitionToState(TelephoneEnumsT::S_DIGIT_COLLECTION);
pPhone->SetPreviousEvent(event);
} // end try
catch (exception& Error) {
stringstream logErr;
logErr << "Exception at OffHookAtIdle(), Error : " << Error.what();
}
return(0);
}
int DigitCollect(BasicFsm* pFsm, string event, int info)
{
stringstream logMsg;
try {
Telephone* pPhone = static_cast <Telephone*> (pFsm);
logMsg << "In Function DigitCollect() " << " At State "
<< pFsm->GetState() << " For Event " << event
<< " adding digit to dialed number " << info << endl;
cout << logMsg.str() << endl;
// Do the required Processing
char cinfo[8];
sprintf(cinfo,"%d",info);
pPhone->digitsCollected.append((const char *) (&cinfo), (size_t)(1));
// Do state transition and do some book keeping
pPhone->SetPreviousEvent(event);
} // end try
catch (exception& Error) {
stringstream logErr;
logErr << "Exception at DigitCollect(), Error : " << Error.what();
}
return(0);
}
int DigitCollectComplete(BasicFsm* pFsm, string event, int info)
{
// Do the required Processing
return(0);
}
int TimeoutAtDigitCollection(BasicFsm* pFsm, string event, int info)
{
// Do the required Processing
return(0);
}
int OffHookAtEstablished(BasicFsm* pFsm, string event, int info)
{
// Do the required Processing
return(0);
}
void Telephone::InitFsm()
{
AddStateEventMethod(TelephoneEnumsT::S_Idle,
TelephoneEnumsT::E_OFFOOK,
&(OffHookAtIdle));
AddStateEventMethod(TelephoneEnumsT::S_DIGIT_COLLECTION,
TelephoneEnumsT::E_DIGIT_COLLECT,
&(DigitCollect));
// Add State - Event functions so forth...
}
int main()
{
int fsmStatus;
Telephone myPhone1("1112223333");
myPhone1.InitFsm();
// Test some event handlers
myPhone1.TransitionToState(S_Idle);
fsmStatus = myPhone1.Fsm(E_OFFOOK, 0);
fsmStatus = myPhone1.Fsm(E_DIGIT_COLLECT, 9);
Telephone myPhone2("2223331234");
myPhone2.InitFsm();
// Test some event handlers
myPhone2.TransitionToState(S_Idle);
fsmStatus = myPhone2.Fsm(E_OFFOOK, 1);
fsmStatus = myPhone2.Fsm(E_DIGIT_COLLECT, 2);
myPhone1.DispDigitsCollected();
myPhone2.DispDigitsCollected();
// Authors comment: try to modify the base class, which can still be
// inherited by any FSM implementing classes
// where you just need to do Telephone::InitFsm();
// instead of doing myPhone1.InitFsm(); and myPhone2.InitFsm(); ...
}
/////// End File : BasicFsmTest.cpp //////////////////////////////////////////