#ifndef __SPFSM_H #define __SPFSM_H #pragma once #include "SpBase.h" #include "ListEntry.h" #ifndef PARAM_SIZE_DEFINED #define PARAM_SIZE_DEFINED #ifdef _WIN32 typedef int param_size_t; #else #if (defined(__x86_64__) || defined(__aarch64__)) typedef intptr_t param_size_t; #elif defined(__i386__) typedef int param_size_t; #else typedef intptr_t param_size_t; #endif #endif //_WIN32 #endif // !PARAM_SIZE_DEFINED /** finite state machine */ enum FSMEventEnum { EVT_INTERNAL, // internal usage EVT_TIMER, // timer evt EVT_MAINPAGE_DISPLAY, //the business main page display EVT_USER, // use defin Event must from here }; #define FSM_STATE_INIT -1 // the initial state of the FSM #define FSM_STATE_EXIT -2 // the exit state of the FSM #define FSM_STATE_ERROR -3 // the error state of the FSM struct FSMEvent { int iEvt; param_size_t param1; param_size_t param2; FSMEvent(int evt) : m_ref_cnt(1) , iEvt(evt) ,param1(-1) /** NOTICES!! maybe make some noise for real online FSM [5/26/2020 Gifur] */ ,param2(-1) /** NOTICES!! maybe make some noise for real online FSM [5/26/2020 Gifur] */ , m_bHandled(FALSE) { m_entry.Flink = m_entry.Blink = &m_entry; } virtual ~FSMEvent() {} void IncRef() { InterlockedIncrement(&m_ref_cnt); } void DecRef() { if (0 == InterlockedDecrement(&m_ref_cnt)) {delete this;} } void SetHandled(BOOL bHandled = TRUE) { m_bHandled = bHandled; } BOOL IsHandled() { return m_bHandled; } virtual void OnUnhandled() {} private: BOOL m_bHandled; LONG m_ref_cnt; LIST_ENTRY m_entry; friend class FSMBase; }; template struct FSMStateEntryT { int id; const char *pszName; void *pUserData; void (T::*pOnEntry)(); void (T::*pOnExit)(); unsigned int (T::*pOnEvent)(FSMEvent *e); }; struct FSMRuleEntry { int src_state; int dst_state; int evt; unsigned int result_start; unsigned int result_end; }; struct IFSMStateHooker { virtual void OnStateTrans(int iSrcState, int iDstState)=0; }; class SPBASE_API FSMBase { public: FSMBase(); virtual ~FSMBase(); ErrorCodeEnum Init(CEntityBase *pEntity); // try start the state machine ErrorCodeEnum PostExitEvent(); // active close FSM, force it into FSM_STATE_EXIT, so OnExit() could be called CEntityBase *GetEntityBase() { return m_pEntity; } void PostEventLIFO(FSMEvent *e); void PostEventFIFO(FSMEvent *e); ErrorCodeEnum ScheduleTimer(int iTimerId, unsigned int timeout); // iTimerId user define, one shot timer ErrorCodeEnum CancelTimer(int iTimerId); void AddStateHooker(IFSMStateHooker *pHooker); void RemoveStateHooker(IFSMStateHooker *pHooker); virtual int GetInitState() = 0; virtual ErrorCodeEnum OnInit() { return Error_Succeed; } virtual ErrorCodeEnum OnExit() { return Error_Succeed; } virtual void OnStateEntry(int state)=0; virtual void OnStateExit(int state)=0; virtual unsigned int OnStateEvent(FSMEvent *e)=0; virtual int MatchRule(int state, int evt, unsigned int rc)=0; virtual BOOL FindStateEvent(int evt)=0; protected: const bool InNotInitState() const { return (m_iState == FSM_STATE_INIT || m_iState == FSM_STATE_EXIT || m_iState == FSM_STATE_ERROR); } private: void Trans(int next); #ifdef _WIN32 #if (!defined(SPABASE_LINKED_AS_STATIC_LIBRARY) && !defined(SPBASE_EXPORTS)) private: #else public: #endif #else public: #endif //_WIN32 void __ProcessEvent(FSMEvent *e); protected: virtual void __PostInit() { } protected: int m_iState; CEntityBase *m_pEntity; private: void OnHook(int iSrcState, int iDstState); enum {MAX_HOOKER = 32}; IFSMStateHooker *m_arrHookers[MAX_HOOKER]; int m_iHookerCnt; void *m_strand; // make sure FSM event execute at one thread!!! LIST_ENTRY m_timerlist; LIST_ENTRY m_eventlist; LONG m_lInTrans; }; template class FSMImpl : public FSMBase { public: virtual void OnStateEntry(int state) { T *pT = static_cast(this); #ifdef _WIN32 T::FSMStateEntry* entry = pT->GetState(state); #else auto entry = pT->GetState(state); #endif //_WIN32 if (entry->pOnEntry) { (pT->*(entry->pOnEntry))(); } } virtual void OnStateExit(int state) { T *pT = static_cast(this); #ifdef _WIN32 T::FSMStateEntry* entry = pT->GetState(state); #else auto entry = pT->GetState(state); #endif //_WIN32 if (entry->pOnExit) { (pT->*(entry->pOnExit))(); } } virtual unsigned int OnStateEvent(FSMEvent *e) { T *pT = static_cast(this); #ifdef _WIN32 T::FSMStateEntry* entry = pT->GetCurrState(); #else auto entry = pT->GetCurrState(); #endif //_WIN32 return (pT->*(entry->pOnEvent))(e); } virtual int MatchRule(int state, int evt, unsigned int rc) { T *pT = static_cast(this); int n = pT->GetRuleCount(); for (int i = 0; i < n; ++i) { FSMRuleEntry *entry = pT->GetRule(i); if (state == entry->src_state) { if (evt == entry->evt) { if (rc >= entry->result_start && rc <= entry->result_end) return entry->dst_state; } } } return state; // no match } virtual BOOL FindStateEvent(int evt) { T *pT = static_cast(this); int n = pT->GetRuleCount(); for (int i = 0; i < n; ++i) { FSMRuleEntry *entry = pT->GetRule(i); if (entry->src_state == m_iState && entry->evt == evt) return TRUE; } return FALSE; } const char *GetStateName(int iState) { if (iState == FSM_STATE_INIT) { return "State_Start"; } else if (iState == FSM_STATE_EXIT) { return "State_End"; } else if (iState == FSM_STATE_ERROR) { return "State_Error"; } else { T *pT = static_cast(this); #ifdef _WIN32 T::FSMStateEntry* entry = pT->GetState(iState); #else auto entry = pT->GetState(iState); #endif //_WIN32 return entry ? entry->pszName : NULL; } } const char *GetCurrStateName() { return GetStateName(m_iState); } }; #define BEGIN_FSM_STATE(cls) \ public: \ typedef FSMStateEntryT FSMStateEntry; \ typedef cls FSM; \ private: \ FSMStateEntry *__EntryTable(int *iNum) { \ static FSMStateEntry tables[] = { #define FSM_STATE_ENTRY(id, name, onentry, onexit, onevent) \ {id, name, NULL, &FSM::onentry, &FSM::onexit, &FSM::onevent}, #define END_FSM_STATE() \ }; \ static int sorted = 0; \ if (!sorted) { \ int n = sizeof(tables)/sizeof(tables[0]); \ for (int i = 0; i < n-1; ++i) { \ FSMStateEntry *t = &tables[i]; \ if (t->id != i) { \ int j; \ for (j = i+1; j < n; ++j) { \ FSMStateEntry *tt = &tables[j]; \ if (tt->id == i) \ break; \ } \ assert(j < n); \ FSMStateEntry tentry; \ memcpy(&tentry, t, sizeof(FSMStateEntry)); \ memcpy(t, &tables[j], sizeof(FSMStateEntry)); \ memcpy(&tables[j], &tentry, sizeof(FSMStateEntry)); \ } \ } \ sorted++; \ } \ if (iNum) \ *iNum = sizeof(tables)/sizeof(tables[0]); \ return &tables[0]; \ } \ public: \ FSMStateEntry *GetCurrState() \ { \ return GetState(m_iState); \ } \ FSMStateEntry *GetState(int iState) \ { \ int num; \ FSMStateEntry *t = __EntryTable(&num); \ assert(iState < num); \ return t + iState; \ } \ int GetStateCount() \ { \ int num; \ __EntryTable(&num); \ return num; \ } #define BEGIN_FSM_RULE(cls, init_state_id) \ public: \ virtual int GetInitState() { return init_state_id; } \ private: \ FSMRuleEntry *__RuleTable(int *iNum) { \ static struct FSMRuleEntry table[] = { #define FSM_RULE_ENTRY(src_state_id, dst_state_id, evt_id, action_result) \ {src_state_id, dst_state_id, evt_id, action_result, action_result}, #define FSM_RULE_ENTRY_RANGE(src_state_id, dst_state_id, evt_id, action_result_start, action_result_end) \ {src_state_id, dst_state_id, evt_id, action_result_start, action_result_end}, #define FSM_RULE_ENTRY_ANY(src_state_id, dst_state_id, evt_id) \ {src_state_id, dst_state_id, evt_id, 0, (unsigned int)-1}, #define END_FSM_RULE() \ }; \ if (iNum) \ *iNum = sizeof(table)/sizeof(table[0]); \ return &table[0]; \ } \ public: \ FSMRuleEntry * GetRule(int iRule) \ { \ int num; \ FSMRuleEntry *t = __RuleTable(&num); \ assert(iRule < num); \ return t + iRule; \ } \ int GetRuleCount() \ { \ int num; \ __RuleTable(&num); \ return num; \ } #endif // __SPFSM_H