/* The external event generator works as a plug-in of the StateWizard engine. Users may customize the external event generator based on the working environment of the user program. */ #include "sme_ext_event.h" #include "sme_cross_platform.h" #include #include typedef struct tagEXTMSG { SME_EVENT_ID_T nMsgID; // 0: stand for empty entity unsigned char nDataFormat ; /* Flag for this event. SME_EVENT_DATA_FORMAT_INT=0, SME_EVENT_DATA_FORMAT_PTR*/ unsigned char nCategory ; /* Category of this event. */ union SME_EVENT_DATA_T Data; SME_OBJ_T *pDestObj; unsigned long nSequenceNum; SME_THREAD_CONTEXT_T* pDestThread; } X_EXT_MSG_T; /* Note: Each thread has an independent message pool, an event/condition and a Mutex. When a new external event post, set the event/condition signaled. The Mutex for a thread pool is to synchronize the message pool access between the event sending thread and the receiving thread. */ #define MSG_BUF_SIZE 100 typedef struct tagEXTMSGPOOL { int nMsgBufHdr; int nMsgBufRear; X_EXT_MSG_T MsgBuf[MSG_BUF_SIZE]; XEVENT EventToThread; XMUTEX MutexForPool; } X_EXT_MSG_POOL_T; /////////////////////////////////////////////////////////////////////////////////////////// // nMsgBufHdr (Get from the head) <============== (Append to the rear) nMsgBufRear /////////////////////////////////////////////////////////////////////////////////////////// /* Initialize the external event buffer at the current thread. */ BOOL XInitMsgBuf(void) { SME_THREAD_CONTEXT_T* pThreadContext = XGetThreadContext(); X_EXT_MSG_POOL_T *pMsgPool; if (NULL!=pThreadContext->pExtEventPool) /* Prevent from creating more than once. */ return FALSE; pThreadContext->pExtEventPool = (void*)malloc(sizeof(X_EXT_MSG_POOL_T)); pMsgPool =(X_EXT_MSG_POOL_T*)(pThreadContext->pExtEventPool); if (NULL==pMsgPool) return FALSE; memset(pMsgPool, 0, sizeof(X_EXT_MSG_POOL_T)); XCreateMutex(&(pMsgPool->MutexForPool)); XCreateEvent(&(pMsgPool->EventToThread)); return TRUE; } /* Free the external event buffer at the current thread. */ BOOL XFreeMsgBuf(void) { SME_THREAD_CONTEXT_T* pThreadContext = XGetThreadContext(); if (NULL!=pThreadContext && NULL!=pThreadContext->pExtEventPool) { free(pThreadContext->pExtEventPool); pThreadContext->pExtEventPool= NULL; return TRUE; } return FALSE; } /* Is message available at the current thread event pool?*/ static BOOL XIsMsgAvailable(void *pArg) { SME_THREAD_CONTEXT_T* p = XGetThreadContext(); X_EXT_MSG_POOL_T *pMsgPool; if (NULL==p || NULL== p->pExtEventPool) return FALSE; SME_UNUSED_VOIDP_PARAM(pArg); pMsgPool = (X_EXT_MSG_POOL_T*)p->pExtEventPool; if (pMsgPool->nMsgBufHdr==pMsgPool->nMsgBufRear) return FALSE; // empty buffer. return TRUE; } /* Thread-safe action to append an external event to the rear of the queue at the destination thread. Timer event overflow prevention. */ static void XAppendMsgToBuf(void *pArg) { X_EXT_MSG_T *pMsg = (X_EXT_MSG_T*)pArg; int nHdr; X_EXT_MSG_POOL_T *pMsgPool; if (NULL==pMsg || NULL==pMsg->pDestThread || NULL==pMsg->pDestThread->pExtEventPool) return; pMsgPool = (X_EXT_MSG_POOL_T*)(pMsg->pDestThread->pExtEventPool); if (((pMsgPool->nMsgBufRear+1) % MSG_BUF_SIZE) == pMsgPool->nMsgBufHdr) return; // buffer full. // Prevent duplicate SME_EVENT_TIMER event triggered by a timer in the queue. nHdr = pMsgPool->nMsgBufHdr; if (SME_EVENT_TIMER == pMsg->nMsgID) { while (nHdr != pMsgPool->nMsgBufRear) { if (SME_EVENT_TIMER == pMsgPool->MsgBuf[nHdr].nMsgID && pMsg->nSequenceNum == pMsgPool->MsgBuf[nHdr].nSequenceNum) return; nHdr = (nHdr+1) % MSG_BUF_SIZE; } } memcpy(&(pMsgPool->MsgBuf[pMsgPool->nMsgBufRear]),pMsg,sizeof(X_EXT_MSG_T)); pMsgPool->nMsgBufRear = (pMsgPool->nMsgBufRear+1)%MSG_BUF_SIZE; } /* Thread-safe action to remove an external event from the current thread event pool.*/ static void XGetMsgFromBuf(void *pArg) { X_EXT_MSG_T *pMsg = (X_EXT_MSG_T*)pArg; SME_THREAD_CONTEXT_T* p = XGetThreadContext(); X_EXT_MSG_POOL_T *pMsgPool; if (NULL==pMsg || NULL==p || NULL==p->pExtEventPool) return; pMsgPool = (X_EXT_MSG_POOL_T*)(p->pExtEventPool); if (pMsgPool->nMsgBufHdr==pMsgPool->nMsgBufRear) return; // empty buffer. memcpy(pMsg,&(pMsgPool->MsgBuf[pMsgPool->nMsgBufHdr]),sizeof(X_EXT_MSG_T)); pMsgPool->MsgBuf[pMsgPool->nMsgBufHdr].nMsgID =0; pMsgPool->nMsgBufHdr = (pMsgPool->nMsgBufHdr+1)%MSG_BUF_SIZE; } int XPostThreadExtIntEvent(SME_THREAD_CONTEXT_T* pDestThreadContext, int nMsgID, int Param1, int Param2, SME_OBJ_T *pDestObj, unsigned long nSequenceNum,unsigned char nCategory) { X_EXT_MSG_T Msg; X_EXT_MSG_POOL_T *pMsgPool; if (nMsgID==0 || NULL== pDestThreadContext || NULL==pDestThreadContext->pExtEventPool) return -1; Msg.nMsgID = nMsgID; Msg.pDestObj = pDestObj; Msg.pDestThread = pDestThreadContext; Msg.nSequenceNum = nSequenceNum; Msg.nDataFormat = SME_EVENT_DATA_FORMAT_INT; Msg.nCategory = nCategory; Msg.Data.Int.nParam1 = Param1; Msg.Data.Int.nParam2 = Param2; pMsgPool = (X_EXT_MSG_POOL_T *)(pDestThreadContext->pExtEventPool); XSignalEvent(&(pMsgPool->EventToThread),&(pMsgPool->MutexForPool),(XTHREAD_SAFE_ACTION_T)XAppendMsgToBuf,&Msg); return 0; } int XPostThreadExtPtrEvent(SME_THREAD_CONTEXT_T* pDestThreadContext, int nMsgID, void *pData, int nDataSize, SME_OBJ_T *pDestObj, unsigned long nSequenceNum,unsigned char nCategory) { X_EXT_MSG_T Msg; X_EXT_MSG_POOL_T *pMsgPool; if (nMsgID==0 || pDestThreadContext==NULL || NULL==pDestThreadContext->pExtEventPool) return -1; Msg.nMsgID = nMsgID; Msg.pDestObj = pDestObj; Msg.pDestThread = pDestThreadContext; Msg.nSequenceNum = nSequenceNum; Msg.nCategory = nCategory; Msg.nDataFormat = SME_EVENT_DATA_FORMAT_PTR; if (pData!=NULL && nDataSize>0) { #if SME_CPP Msg.Data.Ptr.pData = new char[nDataSize]; #else Msg.Data.Ptr.pData = malloc(nDataSize); #endif memcpy(Msg.Data.Ptr.pData, pData, nDataSize); Msg.Data.Ptr.nSize = nDataSize; } else { Msg.Data.Ptr.pData = NULL; Msg.Data.Ptr.nSize = 0; } pMsgPool = (X_EXT_MSG_POOL_T *)(pDestThreadContext->pExtEventPool); XSignalEvent(&(pMsgPool->EventToThread),&(pMsgPool->MutexForPool),(XTHREAD_SAFE_ACTION_T)XAppendMsgToBuf,&Msg); return 0; } BOOL XGetExtEvent(SME_EVENT_T* pEvent) { X_EXT_MSG_T NativeMsg; int ret=0; SME_THREAD_CONTEXT_T* p = XGetThreadContext(); X_EXT_MSG_POOL_T *pMsgPool; if (NULL==pEvent || NULL==p || NULL==p->pExtEventPool) return FALSE; pMsgPool = (X_EXT_MSG_POOL_T*)(p->pExtEventPool); memset(&NativeMsg,0,sizeof(NativeMsg)); while (TRUE) { ret = XWaitForEvent(&(pMsgPool->EventToThread), &(pMsgPool->MutexForPool), (XIS_CODITION_OK_T)XIsMsgAvailable, NULL, (XTHREAD_SAFE_ACTION_T)XGetMsgFromBuf,&NativeMsg); if (NativeMsg.nMsgID == SME_EVENT_EXIT_LOOP) { return FALSE; //Request Exit } #ifdef SME_WIN32 #else // Built-in call back timer on Linux else if (SME_EVENT_TIMER == NativeMsg.nMsgID && SME_TIMER_TYPE_CALLBACK == NativeMsg.Data.Int.nParam1) { // Invoke the call back function. SME_TIMER_PROC_T pfnCallback = (SME_TIMER_PROC_T)(NativeMsg.Data.Int.nParam2); (*pfnCallback)(NativeMsg.pDestObj, NativeMsg.nSequenceNum); } #endif else { // Translate the native message to SME event. memset(pEvent,0,sizeof(SME_EVENT_T)); pEvent->nEventID = NativeMsg.nMsgID; pEvent->pDestObj = NativeMsg.pDestObj; pEvent->nSequenceNum = NativeMsg.nSequenceNum; pEvent->nDataFormat = NativeMsg.nDataFormat; pEvent->nCategory = NativeMsg.nCategory; pEvent->bIsConsumed = FALSE; memcpy(&(pEvent->Data),&(NativeMsg.Data), sizeof(union SME_EVENT_DATA_T)); } //printf("External message received. \n"); return TRUE; }; // while (TRUE) } BOOL XDelExtEvent(SME_EVENT_T *pEvent) { if (0==pEvent) return FALSE; if (pEvent->nDataFormat == SME_EVENT_DATA_FORMAT_PTR) { if (pEvent->Data.Ptr.pData) { #if SME_CPP delete pEvent->Data.Ptr.pData; #else free(pEvent->Data.Ptr.pData); #endif pEvent->Data.Ptr.pData=NULL; } } return TRUE; }