Click here to Skip to main content
15,896,063 members
Articles / Desktop Programming / Win32

Visual Modeling of Complex Reactive Systems with Harel UML StateCharts

Rate me:
Please Sign up or sign in to vote.
5.00/5 (7 votes)
8 Sep 2009LGPL310 min read 43.8K   692   35  
This article presents a commercial-grade cross-platform Harel UML StateChart Open-Source application framework named StateWizard for concurrent, distributed, and real-time reactive system development with simplicity, efficiency, and scalability.
/* ==============================================================================================================================
 * This notice must be untouched at all times.
 *
 * Copyright  IntelliWizard Inc. 
 * All rights reserved.
 * LICENSE: LGPL. 
 * Redistributions of source code modifications must send back to the Intelliwizard Project and republish them. 
 * Web: http://www.intelliwizard.com
 * eMail: info@intelliwizard.com
 * We provide technical supports for UML StateWizard users.
 * ==============================================================================================================================*/

#include "sme.h"
#include "sme_debug.h"

#include "sme_cross_platform.h"

#if !SME_CPP && defined(SME_WIN32)
	/* C4055: A data pointer is cast (possibly incorrectly) to a function pointer. This is a level 1 warning under /Za and a level 4 warning under /Ze. */
	#pragma warning( disable : 4055)
#endif
/*******************************************************************************************
* State Machine Engine static variables.
*******************************************************************************************/
static SME_MEM_ALLOC_PROC_T g_fnMAllocProc = NULL;
static SME_MEM_FREE_PROC_T g_fnMFreeProc = NULL;

static SME_SET_THREAD_CONTEXT_PROC g_pfnSetThreadContext=NULL;
SME_GET_THREAD_CONTEXT_PROC g_pfnGetThreadContext=NULL;

static SME_GET_EXT_EVENT_PROC_T  g_pfnGetExtEvent=NULL;
static SME_DEL_EXT_EVENT_PROC_T  g_pfnDelExtEvent=NULL;
static SME_POST_THREAD_EXT_INT_EVENT_PROC_T g_pfnPostThreadExtIntEvent=NULL;
static SME_POST_THREAD_EXT_PTR_EVENT_PROC_T g_pfnPostThreadExtPtrEvent=NULL;
static SME_INIT_THREAD_EXT_MSG_BUF_PROC_T g_pfnInitThreadExtMsgBuf=NULL;
static SME_FREE_THREAD_EXT_MSG_BUF_PROC_T g_pfnFreeThreadExtMsgBuf=NULL;

BOOL DispatchInternalEvents(SME_THREAD_CONTEXT_PT pThreadContext);
BOOL DispatchEventToApps(SME_THREAD_CONTEXT_PT pThreadContext,SME_EVENT_T *pEvent);

static SME_STATE_T* TransitToState(SME_OBJ_T *pObj, SME_STATE_T *pOldState, SME_STATE_T *pNewState, SME_EVENT_T *pEvent,
								   SME_STATE_T *pExplicitNextState,/* IN/OUT */ int* pTranReason);

/*******************************************************************************************
* DESCRIPTION:  Initialize state machine engine given the thread context.
* INPUT:  None.
* OUTPUT: None.
* NOTE: 
*******************************************************************************************/
void SmeInitEngine(SME_THREAD_CONTEXT_PT pThreadContext)
{
	int i;
	if (!pThreadContext) return;

	// For the platform independent engine..
	// Save the thread context pointer to the TLS.
	if (g_pfnSetThreadContext) 
		(*g_pfnSetThreadContext)(pThreadContext);
	else
		XSetThreadContext(pThreadContext); /* By default. */

	memset(pThreadContext,0,sizeof(SME_THREAD_CONTEXT_T));

	pThreadContext->pActObjHdr = NULL;
	pThreadContext->pFocusedObj = NULL;

	pThreadContext->pEventQueueFront=NULL; 
	pThreadContext->pEventQueueRear=NULL;


	pThreadContext->fnOnEventComeHook = NULL; 
	pThreadContext->fnOnEventHandleHook = NULL;

	/*
	if (NULL==pThreadContext->fnGetExtEvent) 
		pThreadContext->fnGetExtEvent = XGetExtEvent; 
	if (NULL==pThreadContext->fnDelExtEvent)
		pThreadContext->fnDelExtEvent = XDelExtEvent;
	*/

	pThreadContext->nThreadID = XGetCurrentThreadId();

	/* Set all event pool are empty. */
	for (i=0; i<SME_EVENT_POOL_SIZE; i++)
		pThreadContext->EventPool[i].nEventID = SME_INVALID_EVENT_ID;
	pThreadContext->nEventPoolIdx =0;

}

/*******************************************************************************************
* DESCRIPTION:  Get an event data buffer from event pool.
* INPUT:  None
* OUTPUT: New event pointer. If pool is used up, return NULL. 
* NOTE: 
*   
*******************************************************************************************/
static SME_EVENT_T *GetAEvent()
{
	SME_EVENT_T *e=NULL;
	int nOldEventPoolIdx;
	SME_THREAD_CONTEXT_PT pThreadContext=NULL;

	if (g_pfnGetThreadContext)
		pThreadContext = (*g_pfnGetThreadContext)();
	if (!pThreadContext) return NULL;

	nOldEventPoolIdx = pThreadContext->nEventPoolIdx;
	do {
		if (pThreadContext->EventPool[pThreadContext->nEventPoolIdx].nEventID == SME_INVALID_EVENT_ID)
		{
			/*This event data buffer is available.*/
			e = &(pThreadContext->EventPool[pThreadContext->nEventPoolIdx]);
			pThreadContext->nEventPoolIdx = (pThreadContext->nEventPoolIdx+1)%SME_EVENT_POOL_SIZE;
			return e;
		} else
		{
			pThreadContext->nEventPoolIdx = (pThreadContext->nEventPoolIdx+1)%SME_EVENT_POOL_SIZE;
			if (pThreadContext->nEventPoolIdx == nOldEventPoolIdx)
				return NULL;
		}
	} while(TRUE);

}
/*******************************************************************************************
* DESCRIPTION:  Create a state machine event.
* INPUT:  event id, parameter1, parameter2, event category, destination object pointer.
* OUTPUT: New event pointer.
* NOTE: 
*   
*******************************************************************************************/
SME_EVENT_T *SmeCreateIntEvent(SME_EVENT_ID_T nEventId,
								   unsigned long nParam1,
								   unsigned long nParam2,
								   SME_EVENT_CAT_T nCategory,
								   SME_OBJ_T *pDestObj)
{
	SME_EVENT_T *e=NULL;

	if (nEventId==SME_INVALID_EVENT_ID)
		return NULL;

	e=GetAEvent();
	if(e)
	{
		e->nEventID=nEventId;
		e->nSequenceNum =0;
		e->pNext = NULL;
		e->Data.Int.nParam1 = nParam1;
		e->Data.Int.nParam2 = nParam2;
		e->pDestObj=pDestObj;
		e->pPortInfo = NULL;
 		e->nOrigin = SME_EVENT_ORIGIN_INTERNAL;
		e->nCategory=nCategory;
		e->nDataFormat = SME_EVENT_DATA_FORMAT_INT;
		e->bIsConsumed = FALSE;
	}
	return e;
}

SME_EVENT_T *SmeCreatePtrEvent(SME_EVENT_ID_T nEventId,
								   void* pData,
								   unsigned long nSize,
								   SME_EVENT_CAT_T nCategory,
								   SME_OBJ_T *pDestObj)
{
	SME_EVENT_T *e=NULL;

	e=GetAEvent();
	if(e)
	{
		e->nEventID=nEventId;
		e->nSequenceNum =0;
		e->pNext = NULL;
		e->Data.Ptr.pData = pData;
		e->Data.Ptr.nSize = nSize;
		e->pDestObj=pDestObj;
		e->pPortInfo = NULL;
		e->nOrigin = SME_EVENT_ORIGIN_INTERNAL; //by default
		e->nCategory=nCategory;
		e->nDataFormat = SME_EVENT_DATA_FORMAT_PTR;
		e->bIsConsumed = FALSE;
	}
	return e;
}

/*******************************************************************************************
* DESCRIPTION:  Delete a event by marking with SME_INVALID_EVENT_ID.
* INPUT:  
* OUTPUT: None.
* NOTE: 
*   
*******************************************************************************************/
BOOL SmeDeleteEvent(SME_EVENT_T *pEvent)
{
	if(!pEvent) return FALSE;
	pEvent->nEventID = SME_INVALID_EVENT_ID;
	return TRUE;
}

/*******************************************************************************************
Utilities for the state 
*******************************************************************************************/
static SME_STATE_T* GetCompState(SME_STATE_T *pSubState)
{
	if (SME_NULL_STATE==pSubState || SME_STYPE_SUB!=pSubState->nStateType)
		return SME_NULL_STATE;

#if SME_CPP	
	return (SME_STATE_T *)(pSubState->pCompState);
#else
	return (SME_STATE_T *)pSubState->pfnFunc;
#endif
}

static SME_EVENT_HANDLER_T GetStateEntryAction(SME_STATE_T *pState)
{
	if (SME_NULL_STATE == pState) 
		return NULL;

	if (SME_IS_PSEUDO_STATE(pState))
		return NULL;

	switch (pState->nStateType)
	{
	case SME_STYPE_LEAF:
	case SME_STYPE_COMP:
		return (SME_EVENT_HANDLER_T)(pState->pfnFunc); /* State Entry Action */
		break;
	case SME_STYPE_SUB:
		{
			SME_STATE_T *pCompState = GetCompState(pState);
			if (NULL!=pCompState) 
			{
				return (SME_EVENT_HANDLER_T)(pCompState->pfnFunc); /* State Entry Action */
			}
		}
		break;
	default:
		return NULL;
	};

	return NULL;
}

static SME_EVENT_HANDLER_T GetStateExitAction(SME_STATE_T *pState)
{
	if (SME_NULL_STATE == pState) 
		return NULL;

	if (SME_IS_PSEUDO_STATE(pState))
		return NULL;

	switch (pState->nStateType)
	{
	case SME_STYPE_LEAF:
	case SME_STYPE_COMP:
		return (SME_EVENT_HANDLER_T)(pState->pfnExitFunc);
		break;
	case SME_STYPE_SUB:
		{
			SME_STATE_T *pCompState = GetCompState(pState);
			if (NULL!=pCompState) 
			{
				return (SME_EVENT_HANDLER_T)(pCompState->pfnExitFunc);
			}
		}
		break;
	default:
		return NULL;
	};

	return NULL;
}

/* Get initial child state, initial action, and state built-in timeout information */
static void GetStateInfo(SME_STATE_T *pState, SME_STATE_T **ppInitChildState, SME_EVENT_HANDLER_T *ppfnInitAction, 
						 int *pTimeout, SME_EVENT_HANDLER_T *ppfnTimeoutAction, SME_STATE_T **ppTimeoutDestState)
{
	SME_EVENT_TABLE_T *pEvtHldTbl;
	SME_STATE_T *pSubState=NULL;
	SME_STATE_T *pState1=NULL;

	if (NULL==pState || NULL==ppInitChildState || NULL==ppfnInitAction)
		return;

	*ppInitChildState = SME_NULL_STATE; /* By default. */

	if (SME_STYPE_SUB == pState->nStateType)
	{
		SME_STATE_T *pCompState = GetCompState(pState);
		/*Note: No event handler table for ortho-composite state */
		if (SME_STYPE_COMP == pCompState->nStateType)
		{
			pSubState = pState;
			pState = pCompState; /* Get Composite State */
		}
		else
			*ppInitChildState = SME_NULL_STATE;
	}
	else if (SME_STYPE_COMP != pState->nStateType)
		*ppInitChildState = SME_NULL_STATE;
	
	pState1 = pState;

	/* COMP table and then SUB table */
	while (pState1 || pSubState)
	{
		if (pState1)
		{
			pEvtHldTbl = pState1->EventTable;
			pState1 = NULL;
		}
		else if (pSubState)
		{
			pEvtHldTbl = pSubState->EventTable;
			pSubState=NULL;
		}

		if (NULL==pEvtHldTbl)
		{
			return;
		}

		/* Traverse the event handler table. */
		while (SME_INVALID_EVENT_ID != pEvtHldTbl->nEventID)
		{
			if (SME_INIT_CHILD_STATE_ID == pEvtHldTbl->nEventID)
			{
				*ppInitChildState = pEvtHldTbl->pNewState;
				*ppfnInitAction = pEvtHldTbl->pHandler;
			} else if (SME_IS_STATE_TIMEOUT_EVENT_ID(pEvtHldTbl->nEventID))
			{
				if (NULL!=pTimeout && NULL!=ppfnTimeoutAction && NULL!=ppTimeoutDestState)
				{
					*pTimeout = SME_GET_STATE_TIMEOUT_VAL(pEvtHldTbl->nEventID);
					*ppfnTimeoutAction = pEvtHldTbl->pHandler;
					*ppTimeoutDestState = pEvtHldTbl->pNewState;
				}
			}
			pEvtHldTbl++;
		}
	}


	/* Can not find an initial child state in a composite state. 
	   A special case: an object with a single state. 
	*/
	SME_ASSERT_MSG(!(SME_STYPE_COMP == pState->nStateType && *ppInitChildState == SME_NULL_STATE && pState->pParent!=NULL)
		, SMESTR_ERR_NO_INIT_STATE_IN_COMP);
	return; 
}

/* Call an event handler, or a conditional function.
If the handler function is NULL, return SME_EVENT_COND_ELSE.
*/
static int CallHandler(SME_EVENT_HANDLER_T pEvtHdl, SME_OBJ_T* pObj, SME_EVENT_T *pEvent)
{
	if(NULL==pEvtHdl)
		return SME_EVENT_COND_ELSE;
#if SME_CPP
	return (pObj->*pEvtHdl)(pObj,pEvent);
#else
	return pEvtHdl(pObj,pEvent);
#endif
}

/* Dynamically create an object. */
SME_OBJ_T* SmeCreateObj(const char* sName, int nNum, SME_STATE_T *pRoot)
{
	SME_OBJ_T *pObj;
	char sNameBuf[SME_MAX_OBJ_NAME_LEN+1];
	memset(sNameBuf, 0, sizeof(sNameBuf));

	if (nNum>=0)
		snprintf(sNameBuf, sizeof(sNameBuf)-1, SME_REGION_NAME_FMT,sName,nNum);
	else
		strncpy(sNameBuf, sName, sizeof(sNameBuf)-1);

#if SME_CPP
	pObj = new SME_OBJ_T(sNameBuf,pRoot);
#else
	pObj = (SME_OBJ_T *)XEmptyMemAlloc(sizeof(SME_OBJ_T));
	if (pObj)
	{
		strncpy(pObj->sName, sNameBuf, SME_MAX_OBJ_NAME_LEN);
		pObj->pRoot = pRoot;
	}
#endif

	return pObj;
}

/* Dynamically destroy an object. */
BOOL SmeDestroyObj(SME_OBJ_T *pObj)
{
	if (pObj)
	{
#if SME_CPP
		delete pObj;
#else
		XMemFree(pObj);
#endif
		return TRUE;
	} else
		return FALSE;
}

typedef struct SME_REGION_THREAD_CONTEXT_T_TAG
{
	SME_THREAD_CONTEXT_T ThreadContext;
	XTHREADHANDLE ThreadHandle;
	const char* sRegionName;
	int nNum;
	SME_STATE_T *pRegionRoot;
	SME_OBJ_T *pOrthoApp; /* The common dummy object for all regions. */
	struct SME_REGION_THREAD_CONTEXT_T_TAG *pNext;
} SME_REGION_THREAD_CONTEXT_T;

typedef struct SME_REGION_PROCESS_CONTEXT_T_TAG
{
	int PID;
	struct SME_REGION_PROCESS_CONTEXT_T_TAG *pNext;
} SME_REGION_PROCESS_CONTEXT_T;

#ifdef SME_WIN32
	static unsigned __stdcall RegionThreadProc(void *Param)
#else
	static void* RegionThreadProc(void *Param)
#endif
{
	SME_REGION_THREAD_CONTEXT_T *pRegionThreadContext = (SME_REGION_THREAD_CONTEXT_T*)(Param);
	SME_OBJ_T* pRegionApp=NULL;

	if (NULL==pRegionThreadContext)
		return 0;

	pRegionApp = SmeCreateObj(pRegionThreadContext->sRegionName,pRegionThreadContext->nNum,pRegionThreadContext->pRegionRoot); 
	SmeThreadLoop(&(pRegionThreadContext->ThreadContext), pRegionApp, NULL, NULL);
	SmeDestroyObj(pRegionApp);
	return 0;
}

void SmeThreadLoop(SME_THREAD_CONTEXT_T* pThreadContext, SME_OBJ_T *pObj, SME_INIT_CALLBACK_T pfnInitProc, void* pParam)
{
	if (NULL==pThreadContext || NULL==pObj)
		return;
	
	SmeInitEngine(pThreadContext); 
	
	(*g_pfnInitThreadExtMsgBuf)();

	if (pfnInitProc)
		(*pfnInitProc)(pParam);

	SmeActivateObj(pObj, NULL);

	SmeRun();

	(*g_pfnFreeThreadExtMsgBuf)();

	XFreeThreadContext(pThreadContext); 
}

#if SME_PROCESS_REGION_SUPPORT
	extern char g_szCurrentProcessPath[];
#endif

/* Enter the orthogonal state */
static BOOL EnterOrthoState(SME_STATE_T *pOrthoState, SME_OBJ_T *pObj)
{
	SME_REGION_CONTEXT_T *pRegion = NULL;
	SME_STATE_T *pCompOrthoState=NULL;
	SME_OBJ_T* pOrthoApp;
	SME_REGION_THREAD_CONTEXT_T *pRegionThreadContext1=NULL;

	if (NULL==pOrthoState)
		return FALSE;

	pCompOrthoState = GetCompState(pOrthoState);
	if (pCompOrthoState->nStateType != SME_STYPE_ORTHO_COMP)
		return FALSE;

	pRegion = (SME_REGION_CONTEXT_T*)(pCompOrthoState->EventTable);

	if (NULL==pRegion)
		return FALSE;

	/* Create a dummy object which is the parent of all region objects.*/
	/* SmeActivateObj(): The new original object will be inserted into the current thread context. 
	   There is no regular children state for an Orthogonal state, 
	   so if a set of active objects with a same parent object, they should be 
	   the regions of an orthogonal state.
	*/
	pOrthoApp = SmeCreateObj(pOrthoState->sStateName, -1, NULL); /* The root of this dummy object is NULL. */
	SmeActivateObj(pOrthoApp,pObj);

	/* Copy the current thread context to the region thread context as the first item in the region thread context list. */
	pRegionThreadContext1 = (SME_REGION_THREAD_CONTEXT_T *)XEmptyMemAlloc(sizeof(SME_REGION_THREAD_CONTEXT_T));
	SME_ASSERT_MSG(NULL!=pRegionThreadContext1, SMESTR_ERR);
	memcpy(&(pRegionThreadContext1->ThreadContext),XGetThreadContext(), sizeof(SME_THREAD_CONTEXT_T));
	pRegionThreadContext1->pOrthoApp = pOrthoApp; 
	pRegionThreadContext1->ThreadHandle = 0;
	pRegionThreadContext1->sRegionName = pOrthoState->sStateName;
	pRegionThreadContext1->nNum = 1;
	pRegionThreadContext1->pRegionRoot = NULL;
	pRegionThreadContext1->pNext =NULL;
	pObj->pRegionThreadContextList = pRegionThreadContext1; /* The fist item in the list. */

	while (pRegion->pRoot)
	{
		int i=0;
		for (i=0; i<pRegion->nInstanceNum; i++)
		{
			switch (pRegion->nRunningMode)
			{
			case SME_RUN_MODE_PARENT_THREAD:
				{
					SME_OBJ_T* pRegionApp = SmeCreateObj(pRegion->sRegionName,(pRegion->nInstanceNum>1)?i:-1,pRegion->pRoot); 
					SmeActivateObj(pRegionApp,pOrthoApp);
				}
				break;
			case SME_RUN_MODE_SEPARATE_THREAD:
				{
					SME_REGION_THREAD_CONTEXT_T *pRegionThreadContext = (SME_REGION_THREAD_CONTEXT_T *)XEmptyMemAlloc(sizeof(SME_REGION_THREAD_CONTEXT_T));
					if (!pRegionThreadContext)
						return FALSE;
					pRegionThreadContext->pRegionRoot = pRegion->pRoot;
					pRegionThreadContext->sRegionName = pRegion->sRegionName;
					pRegionThreadContext->nNum = (pRegion->nInstanceNum>1)?i:-1;
					pRegionThreadContext->pOrthoApp = pOrthoApp;
					pRegionThreadContext->pNext = pRegionThreadContext1->pNext;
					pRegionThreadContext1->pNext = pRegionThreadContext; /* Insert to the list after the first item. */
					XCreateThread(RegionThreadProc, pRegionThreadContext, &(pRegionThreadContext->ThreadHandle));
					XSetThreadPriority(pRegionThreadContext->ThreadHandle,pRegion->nPriority);
				}
				break;
			case SME_RUN_MODE_SEPARATE_PROCESS:
				#if SME_PROCESS_REGION_SUPPORT
				{
					SME_REGION_PROCESS_CONTEXT_T *pRegionProcessContext = (SME_REGION_PROCESS_CONTEXT_T *)XEmptyMemAlloc(sizeof(SME_REGION_PROCESS_CONTEXT_T));
					if (!pRegionProcessContext)
						return FALSE;
					pRegionProcessContext->PID =0;
					XCreateProcess(g_szCurrentProcessPath, &(pRegionProcessContext->PID));
					pRegionProcessContext->pNext = pRegionProcessContext;
					pObj->pChildProcessList = pRegionProcessContext;
				}
				#endif
				break;
			}
		} 
		pRegion++; 
	}

	//SME_CATCH_STATES();
	return TRUE;
}

static int ExitOrthoState(SME_OBJ_T *pObj)
{
	SME_THREAD_CONTEXT_T *pThreadContext = XGetThreadContext();
	SME_OBJ_T *p = pThreadContext->pActObjHdr;
	SME_REGION_THREAD_CONTEXT_T *pRegionThreadContext1;
	SME_REGION_THREAD_CONTEXT_T *pChildThreadContext=NULL;

	if (pObj)
	{
		pRegionThreadContext1 = (SME_REGION_THREAD_CONTEXT_T *)(pObj->pRegionThreadContextList);
		SME_ASSERT(pRegionThreadContext1!=NULL);
	}
	else
		return -1;

	/* Free all region objects running at the current thread.*/
	while (p!=NULL)
	{
		if (pRegionThreadContext1 && p->pParent==pRegionThreadContext1->pOrthoApp)
		{
			SmeDeactivateObj(p);
			SmeDestroyObj(p);
			/* Note: pThreadContext->pActObjHdr probably changed. */
			p = pThreadContext->pActObjHdr;
		} else
		{
			p=p->pNext;
		}
	}
	SmeDeactivateObj(pRegionThreadContext1->pOrthoApp);
	SmeDestroyObj(pRegionThreadContext1->pOrthoApp);
	pRegionThreadContext1->pOrthoApp=NULL;

	/* Request all region objects to stop running.*/
	if (pRegionThreadContext1 && pRegionThreadContext1->pNext)
	{
		SME_REGION_THREAD_CONTEXT_T *pNextChild=NULL;

		 /* Post SME_EVENT_EXIT_LOOP event to all region threads.*/
		 pChildThreadContext = pRegionThreadContext1->pNext;
		 while (pChildThreadContext)
		 {
			(*g_pfnPostThreadExtIntEvent)(&(pChildThreadContext->ThreadContext), SME_EVENT_EXIT_LOOP, 0, 0, NULL,0,SME_EVENT_CAT_OTHER);
			pChildThreadContext = pChildThreadContext->pNext;
		 }

		 /* Wait for all region threads to exit.*/
		 pChildThreadContext = pRegionThreadContext1->pNext;
		 while (pChildThreadContext)
		 {
			XWaitForThread(pChildThreadContext->ThreadHandle);
			XCLOSE_HANDLE(pChildThreadContext->ThreadHandle);

			pNextChild = pChildThreadContext->pNext;
			XMemFree(pChildThreadContext);
			pChildThreadContext = pNextChild;
		 }
	}

	XMemFree(pRegionThreadContext1);
	pObj->pRegionThreadContextList = NULL;
	pObj->pChildProcessList = NULL;

	{
		SME_REGION_PROCESS_CONTEXT_T *pProcessContext = (SME_REGION_PROCESS_CONTEXT_T *)(pObj->pChildProcessList);

		while(pProcessContext)
		{
			SME_REGION_PROCESS_CONTEXT_T *pNext = pProcessContext->pNext;
			XKillProcess(pProcessContext->PID);
			XMemFree(pProcessContext);
			pProcessContext = pNext;
		}
	}

	//SME_CATCH_STATES();
	return 0;
}

/*
Return the current region objects and region thread contexts of the given object.

pObj
[in] The given object.

nCount 
[in] Number of region number of the given object.

pRegionObjList 
[out] Pointer to an array of region objects. 

pRegionThreadContextList 
[out] Pointer to an array of the thread context list that regions are running at. 

Note: This function should be call at the thread that the given object is running.

*/
int SmeGetRegions(SME_OBJ_T *pObj, int nCount, SME_OBJ_T *pRegionObjList[], SME_THREAD_CONTEXT_T *pRegionThreadContextList[])
{
	SME_THREAD_CONTEXT_T *pThreadContext = XGetThreadContext();
	SME_OBJ_T *p = pThreadContext->pActObjHdr;
	SME_REGION_THREAD_CONTEXT_T *pRegionThreadContext1;
	SME_REGION_THREAD_CONTEXT_T *pChildThreadContext=NULL;
	int i=0;

	if (pObj && nCount>0)
		pRegionThreadContext1 = (SME_REGION_THREAD_CONTEXT_T *)(pObj->pRegionThreadContextList);
	else
		return -1;

	/* Free all region objects running at the current thread.*/
	while (p!=NULL)
	{
		if (pRegionThreadContext1 && p->pParent==pRegionThreadContext1->pOrthoApp)
		{
			pRegionObjList[i] = p;
			pRegionThreadContextList[i] = &(pRegionThreadContext1->ThreadContext);
			i++;

			if (i>=nCount)
				return nCount;
		}

		p=p->pNext;
	}

	/* Request all region objects to stop running.*/
	if (pRegionThreadContext1 && pRegionThreadContext1->pNext)
	{
		SME_REGION_THREAD_CONTEXT_T *pNextChild=NULL;

		 /* Post SME_EVENT_EXIT_LOOP event to all region threads.*/
		 pChildThreadContext = pRegionThreadContext1->pNext;
		 while (pChildThreadContext)
		 {
			pRegionObjList[i] = pChildThreadContext->ThreadContext.pActObjHdr;
			pRegionThreadContextList[i] = &(pChildThreadContext->ThreadContext);
			i++;

			if (i>=nCount)
				return nCount;

			pChildThreadContext = pChildThreadContext->pNext;
		 }
	}

	return i;
}

/*******************************************************************************************
* DESCRIPTION: Activate the given object. 
* INPUT:  
*  1) pNew: The new activated object.
*  2) pParent: who starts this new activated object.
* OUTPUT: None.
*  TRUE: Start it successfully.
*  FALSE: Fail to start it.
* NOTE: 
*  The active objects are linked as a stack.
*  NULL <--- Node.pNext  <--- Node.pNext  <--- pThreadContext->pActObjHdr
*******************************************************************************************/
BOOL SmeActivateObj(SME_OBJ_T *pNew, SME_OBJ_T *pParent)
{
	SME_STATE_T *pState;
	SME_THREAD_CONTEXT_PT pThreadContext=NULL;
	int nTranReason = SME_REASON_ACTIVATED;

	if (g_pfnGetThreadContext)
		pThreadContext = (*g_pfnGetThreadContext)();
	if (!pThreadContext) return FALSE;

	if(!pNew || (SME_IS_ACTIVATED(pNew) && pNew->pRoot !=NULL)) return FALSE;

	pNew->pParent = pParent;
    /* Push the new object to active object stack. */
	pNew->pNext=pThreadContext->pActObjHdr;
	pThreadContext->pActObjHdr=pNew;
	
	/* Call default entry functions. */
	pState = pNew->pRoot; /* The new object state. */
	pNew->pState = pNew->pRoot; /* Keep track of the route of state transitions including non-leaf node.*/
	pNew->pHistoryState = pNew->pRoot;
	memset(pNew->StateTimers,0,sizeof(pNew->StateTimers));
	pNew->nStateNum =0;
	pNew->pRegionThreadContextList = NULL;
	pNew->pChildProcessList = NULL;


	while (pState)
	{
		pState = TransitToState(pNew,NULL,pState,NULL,NULL,&nTranReason); 
	}

	/* Dispatch all internal events which may be triggered by entry functions.*/
	DispatchInternalEvents(pThreadContext);
	
	/* Focus on the new object. */
	SME_SET_FOCUS(pNew);

	return TRUE;
}

/*******************************************************************************************
* DESCRIPTION:  De-activate the given object.
* INPUT:  
*  pObj: the object to be de-activated.
* OUTPUT: None.
*  TRUE: Deactivate it successfully.
*  FALSE: Fail to de-activate it.
* NOTE: 
*******************************************************************************************/
BOOL SmeDeactivateObj(SME_OBJ_T *pObj)
{
	SME_OBJ_T *p,*pPre;
	SME_STATE_T *pState;
	SME_THREAD_CONTEXT_PT pThreadContext=NULL;
	SME_EVENT_HANDLER_T pEvtHdl=NULL;

	if (g_pfnGetThreadContext)
		pThreadContext = (*g_pfnGetThreadContext)();
	if (!pThreadContext) return FALSE;

	if (!pObj || (!SME_IS_ACTIVATED(pObj) && pObj->pRoot !=NULL)) return FALSE;

	/* Locate the object in the active object stack.*/
	p=pThreadContext->pActObjHdr;
	pPre=NULL;
	while (p!=NULL)
	{
		/* The object should be located in the leaf of the active object tree.*/
		if (p->pParent == pObj)
		{
			SME_ASSERT_MSG(p->pParent != pObj, SMESTR_ERR_DEACTIVATE_NON_LEAF_OBJ);
			return FALSE;
		}
		/* 1) Get the previous node of pObj.*/
		if (p->pNext == pObj)
			pPre=p;
		/* 2) Move to next object and traverse all objects. Assert pObj is a leaf. */
		p=p->pNext;
	}

	/* Adjust active object stack. */
	if (pObj==pThreadContext->pActObjHdr)
		/* the object is the active object header.*/
		pThreadContext->pActObjHdr=pObj->pNext;
	else
	{
		SME_ASSERT_MSG(NULL!=pPre, SMESTR_ERR);
		pPre->pNext=pObj->pNext;
	}

	/* If de-activated object is a focused object,the de-activated object's
	parent will be new focused object*/
	if	(pObj==pThreadContext->pFocusedObj)
		if (pObj->pParent != NULL)
			SME_SET_FOCUS(pObj->pParent); /* Focus on the parent object.*/
		else 
			pThreadContext->pFocusedObj = NULL; /* No any object focused.*/

	/* Call exit functions from leaf to the root.*/
	pState = pObj->pState; /* It should be leaf. Special case: if the root is NULL, pState is NULL. */
	while (pState!=SME_NULL_STATE)
	{
		/* Kill the state built-in timers. */
		if (0!=pObj->StateTimers[pObj->nStateNum-1])
		{
			XKillTimer(pObj->StateTimers[pObj->nStateNum-1]);
			pObj->StateTimers[pObj->nStateNum-1]=0;
		}
		pObj->nStateNum--;

		pEvtHdl = GetStateExitAction(pState);
		CallHandler(pEvtHdl,pObj,NULL);

		SME_STATE_TRACK(NULL, pObj, pObj->pState, SME_REASON_DEACTIVATED,0); // Log the old state only. No destination state for state exit.

		pState = pState->pParent;
		pObj->pState = pState;
	}; 

	/* Reset the object data.*/
	pObj->pState = SME_NULL_STATE;
	pObj->pHistoryState = SME_NULL_STATE;
	pObj->pNext =NULL;
	pObj->pParent =NULL;
	pObj->pRegionThreadContextList =NULL;
	pObj->pChildProcessList = NULL;

	/* Dispatch all internal events which may be triggered by exit functions.*/
	DispatchInternalEvents(pThreadContext);

	return TRUE;
}

/*******************************************************************************************
* DESCRIPTION: Set an object focused. 
* INPUT:  
* OUTPUT: None.
* NOTE: 
*   Send SME_EVENT_KILL_FOCUS event to old focused object. And send SME_EVENT_SET_FOCUS
*   to the new focused object.
*******************************************************************************************/
#if SME_UI_SUPPORT

BOOL SmeSetFocus(SME_OBJ_T *pObj)
{
	SME_EVENT_T *pEvent1,*pEvent2;
	SME_THREAD_CONTEXT_PT pThreadContext=NULL;

	if (g_pfnGetThreadContext)
		pThreadContext = (*g_pfnGetThreadContext)();
	if (!pThreadContext) return FALSE;

	if (!pObj) return FALSE;

	if (pThreadContext->pFocusedObj)
	{
		pEvent1=SmeCreateIntEvent(SME_EVENT_KILL_FOCUS,
			0, /*(unsigned long)pObj,*/
			0,
			SME_EVENT_CAT_UI,
			pThreadContext->pFocusedObj);
		if (pEvent1!=NULL)
		{
			SmeDispatchEvent(pEvent1,pThreadContext->pFocusedObj);
			SmeDeleteEvent(pEvent1);
		} else return FALSE;
	}

    pEvent2=SmeCreateIntEvent(SME_EVENT_SET_FOCUS,
		0, /*(unsigned long)pThreadContext->pFocusedObj,*/
		0,
		SME_EVENT_CAT_UI,
		pObj);
	if (pEvent2!=NULL)
	{
		SmeDispatchEvent(pEvent2,pObj);
		SmeDeleteEvent(pEvent2);
	}

    pThreadContext->pFocusedObj=pObj;
	return TRUE;
}
#endif /*SME_UI_SUPPORT*/

/*******************************************************************************************
* DESCRIPTION:  Post an event to queue.
* INPUT:  pEvent: An event.
* OUTPUT: None.
* NOTE: 
*   
*******************************************************************************************/
BOOL SmePostEvent(SME_EVENT_T *pEvent)
{
	SME_THREAD_CONTEXT_PT pThreadContext=NULL;

	if (g_pfnGetThreadContext)
		pThreadContext = (*g_pfnGetThreadContext)();
	if (!pThreadContext) return FALSE;

	if (pEvent == NULL) return FALSE;

	if (pThreadContext->pEventQueueRear==NULL) 
	{ 
		/* The first event in queue. */
		pThreadContext->pEventQueueFront=pThreadContext->pEventQueueRear=pEvent;
	}
	else 
	{
		/* Append the event to queue.*/
		pThreadContext->pEventQueueRear->pNext=pEvent;
		pEvent->pNext=NULL;
		pThreadContext->pEventQueueRear=pEvent;
	}
	return TRUE;
}

/*******************************************************************************************
* DESCRIPTION:  Get an event from queue.
* INPUT:  pEvent: An event.
* RETURN: Event in the head of queue. return NULL if not available.
* NOTE: 
*   
*******************************************************************************************/
SME_EVENT_T * GetEventFromQueue()
{
	/* Get an event from event queue if available. */
	SME_EVENT_T *pEvent = NULL;
	SME_THREAD_CONTEXT_PT pThreadContext=NULL;

	if (g_pfnGetThreadContext)
		pThreadContext = (*g_pfnGetThreadContext)();
	if (!pThreadContext) return NULL;

	if (pThreadContext->pEventQueueFront != NULL)
	{
		pEvent = pThreadContext->pEventQueueFront;
		pThreadContext->pEventQueueFront = pThreadContext->pEventQueueFront->pNext;
		/* Set the end of queue to NULL if queue is empty.*/
		if (pThreadContext->pEventQueueFront == NULL)
			pThreadContext->pEventQueueRear = NULL;
	}
	return pEvent;
}

/*******************************************************************************************
 Check the state's event handler table from leaf to root.
 If find it and pass the guard if available, stop searching, no matter there is another handler in parent state's event table.
********************************************************************************************/
static BOOL IsHandlerAvailable(/* IN */SME_STATE_T *pState, SME_OBJ_T *pObj, SME_EVENT_T *pEvent,
						/* OUT */ SME_STATE_T **ppNewState, SME_EVENT_HANDLER_T *ppHandler)
{
	/* Trace back from leaf to root, so as to retrieve all event handler tables.
	 Find what nNewState is */
	SME_STATE_T *pSuperState=SME_NULL_STATE;
	SME_EVENT_TABLE_T *pStateEventTable;
	SME_STATE_T *pCurrState = pState;
	int i=0;
	int nStateDepth = pObj->nStateNum-1;
	BOOL bFoundHandler=FALSE;
	BOOL bSubStateChecked = FALSE;
	if (SME_NULL_STATE==pState || NULL==pEvent || NULL==ppNewState || NULL==ppHandler)
		return FALSE;

	if (SME_IS_PSEUDO_STATE(pState))
		return FALSE;

	/* Check the event handler table in Composite state, and then the table in sub state.*/
	if (SME_STYPE_SUB == pState->nStateType)
	{
		pSuperState = GetCompState(pState);
	}
	
	/* Note: No event handler table for SME_STYPE_ORTHO_COMP */
	if (pSuperState && pSuperState->nStateType==SME_STYPE_COMP)
	{
		pStateEventTable = pSuperState->EventTable;
		bSubStateChecked = FALSE;
	}
	else
	{
		pStateEventTable = pState->EventTable;
		bSubStateChecked = TRUE;
	}

	while (TRUE)
	{
		/* Check the current state's event handler table.*/
		i=0;
		while (pStateEventTable && pStateEventTable[i].nEventID != SME_INVALID_EVENT_ID)
		{
			if (pStateEventTable[i].nEventID == SME_EXPLICIT_EXIT_EVENT_ID(pEvent->nEventID)) 
			{
				/* The Explicit Exit makes nEventID an explicit event going out of pNewState (the source state) instead of 
				a transition from all children of the current composite state.  */
				SME_STATE_T *p = pCurrState;
				BOOL bIsSrcState = FALSE; /* Is the current state is the source state of the explicit exit.*/
				
				/* Check whether the current state is the source state of the explicit exit or its children state. */
				while(SME_NULL_STATE!=p)
				{
					if (p==pStateEventTable[i].pNewState)
					{
						/* It is an explicit exit, proceed with looking for SME_ON_EVENT() for this explicit exit.*/
						bIsSrcState = TRUE;
						break;
					}
					p=p->pParent;
				}

				if (!bIsSrcState)
				{
					/* Not match an explicit exit. */
					SME_STATE_TRACK(pEvent, pObj, pObj->pState, SME_REASON_NOT_MATCH,0);
					return FALSE;
				}
			} else if (
				(pStateEventTable[i].nEventID == pEvent->nEventID) /* Regular events including SME_EVENT_TIMER */
				|| (SME_IS_STATE_TIMEOUT_EVENT_ID(pStateEventTable[i].nEventID) && SME_EVENT_STATE_TIMER==pEvent->nEventID
					&& pEvent->nSequenceNum == pObj->StateTimers[nStateDepth]) /* State built-in timeout events with the corresponding state timer sequence number*/
				) /* Distinguish regular timer and state built-in timer.*/
			{
#if SME_CPP
				if ((0 == pStateEventTable[i].pGuardFunc) /* NULL pointer */
				|| (pObj->pSME_NULL_GUARD == pStateEventTable[i].pGuardFunc) /* pointer to a NULL function. */
				|| (pObj->*pStateEventTable[i].pGuardFunc)(pObj,pEvent) /* Call guard function. */
				)
#else
				if (SME_NULL == pStateEventTable[i].pGuardFunc 
					|| (*pStateEventTable[i].pGuardFunc)(pObj,pEvent)
				)
#endif
				{
					/* Match, and the guard returns TRUE. */
					/* Hit */
					*ppNewState=pStateEventTable[i].pNewState;
					*ppHandler = pStateEventTable[i].pHandler;
					bFoundHandler = TRUE;
					return TRUE;
				} else
				{
					/* Match, however the guard returns FALSE. */
					SME_STATE_TRACK(pEvent, pObj, pObj->pState, SME_REASON_GUARD,0);
					return FALSE;
				}
			} 
			i++;
		} /* Search the event handler table. */
		
		if (!bSubStateChecked)
		{
			/* About to check sub-state */
			pStateEventTable = pState->EventTable;
			bSubStateChecked = TRUE;
		} else
		{
			/* Get the parent state's event handler table. */
			if (SME_NULL_STATE==pState->pParent) break; /* Reach the root. */
			/* Search the parent. */
			pState = pState->pParent;
			nStateDepth--;
			/* SME_STYPE_SUB == pState->nStateType */
			pSuperState = GetCompState(pState);
			pStateEventTable = pSuperState->EventTable;
			bSubStateChecked = FALSE;
		}
	}

	SME_STATE_TRACK(pEvent, pObj, pObj->pState, SME_REASON_NOT_MATCH,0);
	return FALSE;
}

/*******************************************************************************************
 Check whether an explicit entry on the given event is available in a given composite state event handler table, 
 If find it, return the entry child state .
********************************************************************************************/
static BOOL IsExplicitEntryAvailable(/* IN */SME_STATE_T *pState, SME_EVENT_T *pEvent,
						/* OUT */ SME_STATE_T **ppNewState)
{
	SME_EVENT_TABLE_T *pStateEventTable=NULL;
	SME_STATE_T *pCompState=SME_NULL_STATE;

	if (SME_NULL_STATE ==pState || NULL == pEvent || NULL==ppNewState)
		return FALSE;

	if (SME_STYPE_COMP != pState->nStateType && SME_STYPE_SUB != pState->nStateType)
		return FALSE;

	if (SME_STYPE_SUB == pState->nStateType)
	{
		SME_STATE_T *p = GetCompState(pState);
		if (p->nStateType == SME_STYPE_COMP)
			pCompState = GetCompState(pState);
		else pCompState = pState;

	}else
		pCompState = pState;
	
	pStateEventTable = pCompState->EventTable;

	if (pStateEventTable)
	{
		int i=0;
		while (SME_INVALID_EVENT_ID != pStateEventTable[i].nEventID)
		{
			if (SME_EXPLICIT_ENTRY_EVENT_ID(pEvent->nEventID) == pStateEventTable[i].nEventID)
			{
				*ppNewState = pStateEventTable[i].pNewState;
				return TRUE;
			}

			i++;
		}
	}
	return FALSE;
}

/*******************************************************************************************
* DESCRIPTION: Dispatch the incoming event to an object if it is specified, otherwise
*  dispatch to all active objects until it is consumed.  
* INPUT:  
*  1) pEvent: Incoming event 
*  2) pObj: The destination object that event will be dispatched.
* OUTPUT: None.
* NOTE: 
*	1) Call exit functions in old state   
*	2) Call event handler functions   
*	3) Call entry functions in new state  
*  4) Transit from one state region to another state region. All states exit functions that jump out 
*  the old region will be called. And all states exit functions that jump in the new region will be called.
*  5) Although there is a property pEvent->pDestObj in SME_EVENT_T, this function will ignore this one,
*		because if pEvent->pDestObj is NULL, this event have to dispatch to all active objects.
*******************************************************************************************/
BOOL SmeDispatchEvent(SME_EVENT_T *pEvent, SME_OBJ_T *pObj)
{
	SME_STATE_T *pOldState=SME_NULL_STATE; /* Old state should be a leaf.*/
	SME_STATE_T *pState=SME_NULL_STATE;
    SME_STATE_T *pNewState=SME_NULL_STATE;
	int i;
	SME_EVENT_HANDLER_T pHandler = NULL;

	SME_STATE_T *OldStateStack[SME_MAX_STATE_TREE_DEPTH];
	SME_STATE_T *NewStateStack[SME_MAX_STATE_TREE_DEPTH];
	int nOldStateStackTop =0;
	int nNewStateStackTop =0;
	int nRepeatTime=0;
	int nTranReason=SME_REASON_HIT;

	SME_THREAD_CONTEXT_PT pThreadContext=NULL;
	
	#if SME_DEBUG
	int nBeginTick=XGetTick();
	#endif

	if (g_pfnGetThreadContext)
		pThreadContext = (*g_pfnGetThreadContext)();
	if (!pThreadContext) return FALSE;

	if (pEvent==NULL || pObj==NULL) return FALSE;

	pOldState = pObj->pState; /* Old state should be a leaf. */

	if (!IsHandlerAvailable(pOldState, pObj, pEvent, &pNewState, &pHandler)) 
		return FALSE;

	do { /* Loop until the destination state is not a pseudo state .*/

		/* Call the exit functions if it is not an internal transition and the old state is a regular state. */
		if (SME_INTERNAL_TRAN != pNewState)
		{
			if (!SME_IS_PSEUDO_STATE(pNewState))
				pObj->pHistoryState = pOldState; /* The original state before transition.*/

			/* The state stack: 0  the leaf (pOldState) --> the root state . */
			/* It is a state transition.
			 Push all old state's ancestors.
			*/
			nOldStateStackTop =0;
			nNewStateStackTop =0;

			pState = pOldState;
			while (pState!=SME_NULL_STATE)
			{
				OldStateStack[nOldStateStackTop++] = pState;
				pState=pState->pParent;
				if (nOldStateStackTop >= SME_MAX_STATE_TREE_DEPTH)
					return FALSE;
			}

			/* Push all new state's ancestors. */
			pState = pNewState;
			while (pState!=SME_NULL_STATE)
			{
				NewStateStack[nNewStateStackTop++] = pState;
				pState=pState->pParent;
				if (nNewStateStackTop >= SME_MAX_STATE_TREE_DEPTH)
					return FALSE;
			}

			/* Pop all equal states except the last one.
			 Special case 1: self transition state1->state1, leave one item in each stack.
			 Special case 2: a parent state transits to its child state, leave one item in the parent state stack.
			*/
			while ((nOldStateStackTop>1) && (nNewStateStackTop>1)
				&& (OldStateStack[nOldStateStackTop-1] == NewStateStack[nNewStateStackTop-1]))
			{
				nOldStateStackTop--;
				nNewStateStackTop--;
			}

			/* Get the leaf of the old state.
			 Note: Old state should be a leaf state.
			 Call exit functions from the leaf nState to the top state in the old state stack.
			 */
			for (i=0; i<nOldStateStackTop; i++)
			{
				SME_EVENT_HANDLER_T pEvtHdl;
				pState = OldStateStack[i];

				/* Stop state built-in timer. */
				if (0!=pObj->StateTimers[pObj->nStateNum-1])
				{
					XKillTimer(pObj->StateTimers[pObj->nStateNum-1]);
					pObj->StateTimers[pObj->nStateNum-1] =0; /* Clear the state timer. */
				}

				SME_STATE_TRACK(pEvent, pObj, pObj->pState, SME_REASON_DEACTIVATED,0); // No destination state for state exit.

				pObj->nStateNum--;
				pObj->pState = pState; /* Keep track of the route of state transitions including non-leaf node.*/

				if (SME_STYPE_SUB == pState->nStateType)
				{
					SME_STATE_T	*pCompOrthoState = GetCompState(pState);
					if (pCompOrthoState->nStateType == SME_STYPE_ORTHO_COMP)
						/* Exit Orthogonal state*/
						ExitOrthoState(pObj);
				}

				pEvtHdl=GetStateExitAction(pState); /* If it is pseudo state, return NULL. */
				CallHandler(pEvtHdl,pObj,pEvent);

			};
		}; /* end of non internal transition.*/

		/*******************************************************************************************
		 Call event handler function if given event handler is available and handler is not empty.
		 Maybe their is a transition, however handler is empty.
		*/
		CallHandler(pHandler,pObj,pEvent);
		pHandler= NULL;

		if (pNewState == SME_INTERNAL_TRAN)
			SME_STATE_TRACK(pEvent, pObj, pOldState, SME_REASON_INTERNAL_TRAN, XGetTick() - nBeginTick);
		else
		{
			SME_STATE_T *pExplicitNextState=NULL;
			SME_STATE_T *pNextState=NULL;
			/* Explicit entry to a composite state. 
			 Note: Do not update the old state.
			*/
			for (i=nNewStateStackTop-1; i>=0; i--)
			{
				pNewState = NewStateStack[i];
				if (i>=1)
					pExplicitNextState = NewStateStack[i-1];
				else
					pExplicitNextState = NULL;

				pNewState = TransitToState(pObj,pOldState,pNewState,pEvent,pExplicitNextState,&nTranReason);
				/* On loop exit, pNewState will be next state by default.*/
			}

			/* Default entry to a state. */
			while (pNewState)
			{
				pNextState = TransitToState(pObj,pOldState,pNewState,pEvent,NULL,&nTranReason); 
				pOldState = pNewState;
				pNewState = pNextState;
			}
		}

	   nRepeatTime++;
	} while (nRepeatTime<=SME_MAX_PSEUDO_TRAN_NUM && pNewState != SME_INTERNAL_TRAN 
		&& (SME_IS_PSEUDO_STATE(pNewState) || SME_IS_PSEUDO_STATE(pOldState))); 
	/* Loop until the destination state is not a pseudo state. */

	SME_ASSERT_MSG(nRepeatTime<SME_MAX_PSEUDO_TRAN_NUM, SMESTR_ERR_A_LOOP_PSEUDO_STATE_TRAN);

	/*******************************************************************************************
	 Call event handle hook function if given event handler is available and no matter whether handler is empty or not.
	 */
	if (pThreadContext->fnOnEventHandleHook)
		(*pThreadContext->fnOnEventHandleHook)((SME_EVENT_ORIGIN_T)(pEvent->nOrigin), 
			pEvent, 
			pObj, 
			SME_GET_OBJ_STATE(pObj));

	return TRUE;
}

/************************************************************************************************************************************ 
TRIGGERS:
1) Active an object. pOldState==NULL
2) Default State Transition. pExplicitNextState==NULL
3) Explicit State Transition.
*************************************************************************************************************************************/
static SME_STATE_T* TransitToState(SME_OBJ_T *pObj, SME_STATE_T *pOldState, SME_STATE_T *pNewState, SME_EVENT_T *pEvent,
								   SME_STATE_T *pExplicitNextState, /* IN/OUT */ int* pTranReason)
{
	int nBeginTick=XGetTick();
	SME_STATE_T *pNextState=NULL;
	
	if (SME_INTERNAL_TRAN == pNewState)
		return NULL;

	/******************************************************************************************
		New state is a Pseudo State
	*/
	if (SME_STYPE_COND == pNewState->nStateType)
	{
		/* Conditional Pseudo State => a state */
		SME_EVENT_ID_T nCondRet = (SME_EVENT_ID_T)CallHandler((SME_EVENT_HANDLER_T)pNewState->pfnFunc,pObj,pEvent);
		SME_EVENT_TABLE_T *pStateEventTable = pNewState->EventTable;
		int i=0;

		pObj->pState = pNewState; 
		SME_STATE_TRACK(pEvent, pObj, pOldState, *pTranReason, XGetTick() - nBeginTick);
		nBeginTick = XGetTick();
		*pTranReason = SME_REASON_COND;

		/* There should be no built-in state timer in pseudo states. Do not increase nStateNum when enters a pseudo state. */

		while (pStateEventTable && pStateEventTable[i].nEventID != SME_INVALID_EVENT_ID)
		{
			if (nCondRet == pStateEventTable[i].nEventID
				|| SME_EVENT_COND_ELSE == pStateEventTable[i].nEventID)
			{
				/* Call the conditional action function. */
				CallHandler(pStateEventTable[i].pHandler,pObj,pEvent);
				/* Get the destination state */
				pNextState = pStateEventTable[i].pNewState;
				/* The SME_EVENT_COND_ELSE probably is not located at the last item in the condition list. */
				if (nCondRet == pStateEventTable[i].nEventID)
					break;
			}
			i++;
		}
		SME_ASSERT_MSG(pStateEventTable[i].nEventID != SME_INVALID_EVENT_ID, SMESTR_ERR_FAIL_TO_EVAL_COND);

	} else if (SME_STYPE_JOIN == pNewState->nStateType)
	{
		/* Join Pseudo State => a state */
		SME_EVENT_TABLE_T *pStateEventTable = pNewState->EventTable;

		pObj->pState = pNewState; 
		SME_STATE_TRACK(pEvent, pObj, pOldState, *pTranReason, XGetTick() - nBeginTick);
		nBeginTick = XGetTick();
		*pTranReason = SME_REASON_JOIN;

		/* There should be no built-in state timer in pseudo states. Do not increase nStateNum when enters a pseudo state. */

		if (pStateEventTable && SME_JOIN_STATE_ID == pStateEventTable[0].nEventID)
		{
			/* Call the conditional action function. */
			CallHandler(pStateEventTable[0].pHandler,pObj,pEvent);
			/* Get the destination state */
			pNextState = pStateEventTable[0].pNewState;

			SME_ASSERT_MSG(SME_NULL_STATE != pNewState, SMESTR_ERR_NO_JOIN_TRAN_STATE);
		} else
		{
			/* ERROR */
			SME_ASSERT_MSG(TRUE, SMESTR_ERR_NO_JOIN_TRAN_STATE);
		}

	} else if (!SME_IS_PSEUDO_STATE(pNewState))
	{
		/******************************************************************************************
			New state is a Regular State or Orthogonal state.
		*/
		SME_STATE_T *pChildState=NULL;
		SME_EVENT_HANDLER_T pfnDefSubStateAction=NULL;
		int nStateTimeOut=-1;
		SME_EVENT_HANDLER_T pfnStateTimeoutAction=NULL;
		SME_STATE_T *pTimeOutDestState=NULL;

		/* Keep track of the route of state transitions including non-leaf node.*/
		pObj->pHistoryState = pOldState; /* The destination state after transition.*/
		pObj->pState = pNewState;
		GetStateInfo(pNewState, &pChildState, &pfnDefSubStateAction, &nStateTimeOut, &pfnStateTimeoutAction, &pTimeOutDestState);

		/* Start the state built-in timer. */
		if (nStateTimeOut!=-1)
		{
			/* Set a state built-in timer. */
			pObj->StateTimers[pObj->nStateNum] = XSetEventTimer(pObj,SME_STATE_BUILT_IN_TIMEOUT_VAL(nStateTimeOut)); 
			SME_ASSERT_MSG(0!=pObj->StateTimers[pObj->nStateNum], SMESTR_ERR_FAIL_TO_SET_TIMER);
		}
		pObj->nStateNum++;

		CallHandler(GetStateEntryAction(pNewState), pObj,pEvent);
		if (SME_STYPE_SUB==pNewState->nStateType)
		{
			SME_STATE_T	*pCompOrthoState = GetCompState(pNewState);
			if (pCompOrthoState->nStateType == SME_STYPE_ORTHO_COMP)
				/* Orthogonal state */
				EnterOrthoState(pNewState,pObj);
		}

		SME_STATE_TRACK(pEvent, pObj, pOldState, *pTranReason, XGetTick() - nBeginTick);
		*pTranReason = SME_REASON_ACTIVATED;

		if (NULL==pExplicitNextState)
		{
			pNextState = pChildState;
			/* pNewState is a composite state. */
			if (NULL!=pChildState)
			{
				/* Explicit entry to a specific child state. */
				if (!IsExplicitEntryAvailable(pNewState,pEvent,&pNextState))
				{
					/* Call the action for the initial sub-state. */
					CallHandler(pfnDefSubStateAction,pObj,pEvent);
					pNextState = pChildState;
				} /* Note: pNextState is the explicit entry state */
			} else
			{
				/* Call the action for the initial sub-state. */
				CallHandler(pfnDefSubStateAction,pObj,pEvent);
				pNextState = pChildState;
			}
		} else
		{
			pNextState = pExplicitNextState;
		}

	} /* End of regular state. */

	return pNextState;
}
/*******************************************************************************************
* DESCRIPTION:  This API function installs getting and deleting external event functions. 
*  It will never exit. 
* INPUT:  
* OUTPUT: None.
* NOTE: 
*   
*******************************************************************************************/
void SmeSetExtEventOprProc(SME_GET_EXT_EVENT_PROC_T fnGetExtEvent, 
						   SME_DEL_EXT_EVENT_PROC_T fnDelExtEvent,
	SME_POST_THREAD_EXT_INT_EVENT_PROC_T fnPostThreadExtIntEvent,
	SME_POST_THREAD_EXT_PTR_EVENT_PROC_T fnPostThreadExtPtrEvent,
	SME_INIT_THREAD_EXT_MSG_BUF_PROC_T fnInitThreadExtMsgBuf,
	SME_INIT_THREAD_EXT_MSG_BUF_PROC_T fnFreeThreadExtMsgBuf)
{
	g_pfnGetExtEvent = fnGetExtEvent;
	g_pfnDelExtEvent = fnDelExtEvent;

	g_pfnPostThreadExtIntEvent = fnPostThreadExtIntEvent;
	g_pfnPostThreadExtPtrEvent = fnPostThreadExtPtrEvent;

	g_pfnInitThreadExtMsgBuf = fnInitThreadExtMsgBuf;
	g_pfnFreeThreadExtMsgBuf = fnFreeThreadExtMsgBuf;
}

int SmePostThreadExtIntEvent(SME_THREAD_CONTEXT_T* pDestThreadContext, int nMsgID, int Param1, int Param2, 
						   SME_OBJ_T *pDestObj, unsigned long nSequenceNum,unsigned char nCategory)
{
	if (g_pfnPostThreadExtIntEvent)
		return (*g_pfnPostThreadExtIntEvent)(pDestThreadContext, nMsgID, Param1, Param2, 
						   pDestObj, nSequenceNum, nCategory);
	else
		return -1;
}

int SmePostThreadExtPtrEvent(SME_THREAD_CONTEXT_T* pDestThreadContext, int nMsgID, void *pData, int nDataSize, 
						   SME_OBJ_T *pDestObj, unsigned long nSequenceNum,unsigned char nCategory)
{
	if (g_pfnPostThreadExtPtrEvent)
		return (*g_pfnPostThreadExtPtrEvent)(pDestThreadContext, nMsgID, pData, nDataSize, 
						   pDestObj, nSequenceNum, nCategory);
	else
		return -1;
}

/*******************************************************************************************
* DESCRIPTION:  This API function is the state machine engine event handling loop function. 
*  It will never exit. 
* INPUT:  
* OUTPUT: None.
* NOTE: 

Engine has to check internal event first, because SmeActivateObj() may trigger some internal events.

Case:
	SmeActivateObj(&Player1,NULL);
	SmeRun();
  
*******************************************************************************************/
void SmeRun(void)
{
	SME_EVENT_T ExtEvent;
	SME_EVENT_T *pEvent=NULL;
	SME_OBJ_T *pObj;
	
	SME_THREAD_CONTEXT_PT pThreadContext=NULL;
	if (g_pfnGetThreadContext)
		pThreadContext = (*g_pfnGetThreadContext)();
	if (!pThreadContext) return;

	if (!g_pfnGetExtEvent) return;

	pObj = pThreadContext->pActObjHdr;

	while (TRUE)
	{
		/* Check the internal event pool firstly. */
		pEvent = GetEventFromQueue();
		if (pEvent == NULL)
		{
			/* Wait for an external event. */
			if (FALSE == (*g_pfnGetExtEvent)(&ExtEvent)) 
				return; // Exit the thread.

			pEvent = &ExtEvent;
			pEvent->nOrigin = SME_EVENT_ORIGIN_EXTERNAL;

			/* Call hook function on an external event coming. */
			if (pThreadContext->fnOnEventComeHook)
				(*pThreadContext->fnOnEventComeHook)(SME_EVENT_ORIGIN_EXTERNAL, pEvent);
		}else
		{
			/* Call hook function on an internal event coming. */
			if (pThreadContext->fnOnEventComeHook)
				(*pThreadContext->fnOnEventComeHook)(SME_EVENT_ORIGIN_INTERNAL, pEvent);
		}

		do { 
			DispatchEventToApps(pThreadContext, pEvent);

			/* Free internal event. Free external event later. */
			if (pEvent != &ExtEvent)
				SmeDeleteEvent(pEvent);

			/* Get an event from event queue if available. */
			pEvent = GetEventFromQueue();
			if (pEvent != NULL)
			{
				/* Call hook function on an internal event coming. */
				if (pThreadContext->fnOnEventComeHook)
					(*pThreadContext->fnOnEventComeHook)(SME_EVENT_ORIGIN_INTERNAL, pEvent);
			}
			else 
			{
				/* The internal event queue is empty. */
				break;
			}
		} while (TRUE); /* Get all events from the internal event pool. */

		/* Free external event if necessary. */
		if (g_pfnDelExtEvent)
		{
			(*g_pfnDelExtEvent)(&ExtEvent);
			// Engine should delete this event, because translation of external event will create an internal event. 
			SmeDeleteEvent(&ExtEvent); 
		}

	} /* Wait for an external event. */
}

/*******************************************************************************************
* DESCRIPTION:   
* INPUT:  
* OUTPUT: None.
* NOTE: SmeRun() and SmeActivateObj()/SmeDeactivateObj() calls this function.  
********************************************************************************************/
BOOL DispatchInternalEvents(SME_THREAD_CONTEXT_PT pThreadContext)
{
	SME_EVENT_T *pEvent=NULL;
	SME_OBJ_T *pObj;
	
	if (!pThreadContext) return FALSE;

	pObj = pThreadContext->pActObjHdr;

	pEvent = GetEventFromQueue();
	while (pEvent != NULL)
	{
		pEvent->nOrigin = SME_EVENT_ORIGIN_INTERNAL;
		/* Call hook function on an internal event coming. */
		if (pThreadContext->fnOnEventComeHook)
			(*pThreadContext->fnOnEventComeHook)(SME_EVENT_ORIGIN_INTERNAL, pEvent);
		
		DispatchEventToApps(pThreadContext, pEvent);

		/* Free internal event*/
		SmeDeleteEvent(pEvent);
		/* Next internal event? */
		pEvent = GetEventFromQueue();
	}
	return TRUE;
}

/*******************************************************************************************
* DESCRIPTION:   
* INPUT:  
* OUTPUT: None.
* NOTE: SmeRun() and SmeActivateObj()/SmeDeactivateObj() calls this function.  
********************************************************************************************/
BOOL DispatchEventToApps(SME_THREAD_CONTEXT_PT pThreadContext,SME_EVENT_T *pEvent)
{
	SME_OBJ_T *pObj;
	if (pThreadContext==NULL || pEvent==NULL) return FALSE;

	/*Dispatch it to active objects*/
	if (pEvent->pDestObj)
	{
		/* This event has destination object. Dispatch it if the object is active.*/
		if (SME_IS_ACTIVATED(pEvent->pDestObj))
			/* Dispatch it to an destination object. */
			SmeDispatchEvent(pEvent, pEvent->pDestObj);
	}
	else if (pEvent->nCategory == SME_EVENT_CAT_UI)
		/* Dispatch UI event to the focused object. */
		SmeDispatchEvent(pEvent, pThreadContext->pFocusedObj);
	else 
	{
		/* Traverse all active objects. */
		pObj = pThreadContext->pActObjHdr;
		while (pObj != NULL) 
		{
			SmeDispatchEvent(pEvent, pObj);
			if (pEvent->bIsConsumed) 
				break;
			else pObj = pObj->pNext; 
		}
	}
	return TRUE;
}
/*******************************************************************************************
* DESCRIPTION:  This API function install a hook function. It will be called when event comes. 
* INPUT:  
* OUTPUT: None.
* NOTE: 
*   
*******************************************************************************************/
SME_ON_EVENT_COME_HOOK_T SmeSetOnEventComeHook(SME_ON_EVENT_COME_HOOK_T fnHook)
{
	SME_ON_EVENT_COME_HOOK_T fnOldHook;
	SME_THREAD_CONTEXT_PT pThreadContext=NULL;
	if (g_pfnGetThreadContext)
		pThreadContext = (*g_pfnGetThreadContext)();
	if (!pThreadContext) return NULL;

	fnOldHook = pThreadContext->fnOnEventComeHook;
	pThreadContext->fnOnEventComeHook = fnHook;
	return fnOldHook;
}

/*******************************************************************************************
* DESCRIPTION:  This API function install a hook function. It will be called when event is handled. 
* INPUT:  
* OUTPUT: None.
* NOTE: 
*   
*******************************************************************************************/
SME_ON_EVENT_HANDLE_HOOK_T SmeSetOnEventHandleHook(SME_ON_EVENT_HANDLE_HOOK_T fnHook)
{
	SME_ON_EVENT_HANDLE_HOOK_T fnOldHook;
	SME_THREAD_CONTEXT_PT pThreadContext=NULL;
	if (g_pfnGetThreadContext)
		pThreadContext = (*g_pfnGetThreadContext)();
	if (!pThreadContext) return NULL;

	fnOldHook = pThreadContext->fnOnEventHandleHook;
	pThreadContext->fnOnEventHandleHook = fnHook;
	return fnOldHook;
}
/*******************************************************************************************
* DESCRIPTION:  This API function set an event consumed. It will not dispatched to another object. 
* INPUT:  
* OUTPUT: None.
* NOTE: 
*   
*******************************************************************************************/
void SmeConsumeEvent(SME_EVENT_T *pEvent)
{
	if (!pEvent) return;
	pEvent->bIsConsumed = TRUE;
}

/*******************************************************************************************
* DESCRIPTION:  This API function sets memory allocation and free function pointers. 
* INPUT: fnMAllocProc: Memory allocation function pointer;
*	      fnMFreeProc: 	Memory free function pointer.	 
* OUTPUT: None.
* NOTE: 
*   
*******************************************************************************************/
void SmeSetMemOprProc(SME_MEM_ALLOC_PROC_T fnMAllocProc, SME_MEM_FREE_PROC_T fnMFreeProc)
{
	g_fnMAllocProc= fnMAllocProc;
	g_fnMFreeProc = fnMFreeProc;
}

/*******************************************************************************************
* DESCRIPTION:  State machine engine memory allocation function in release version. 
* INPUT: nSize: Memory size;
* OUTPUT: Allocated memory pointer.
* NOTE: It will just call user defined memory allocation function.
*   
*******************************************************************************************/
void* SmeMAllocRelease(unsigned int nSize)
{
	if (g_fnMAllocProc)
		return (*g_fnMAllocProc)(nSize);
	else return NULL;
}

/*******************************************************************************************
* DESCRIPTION:  State machine engine memory free function in release version. 
* INPUT: pUserData: Memory block pointer;
* OUTPUT: The result depends on user defined memory free function.
* NOTE: It will just call user defined memory free function.
*   
*******************************************************************************************/
BOOL SmeMFreeRelease(void * pUserData)
{
	if (g_fnMFreeProc)
		return (*g_fnMFreeProc)(pUserData);
	else return FALSE;
}

/*******************************************************************************************
* DESCRIPTION:  This API function sets thread local storage function pointers. 
* INPUT: pSetThreadContextProc: Thread context setting function pointer;
*	      pGetThreadContext: 	Thread context getting function pointer.	 
* OUTPUT: None.
* NOTE: 
*   
*******************************************************************************************/
void SmeSetTlsProc(SME_SET_THREAD_CONTEXT_PROC pfnSetThreadContext, SME_GET_THREAD_CONTEXT_PROC pfnGetThreadContext)
{
	g_pfnSetThreadContext = pfnSetThreadContext;
	g_pfnGetThreadContext = pfnGetThreadContext;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions