/** * Event Handling Function. * * Handles the event at current state. Handles message at the current * state. If the message/event is not handled at current state, then * its passed to the parent and passed till 'top'. If its not * handled, till top, then this method returns ERROR. else, the * transition is handled. Transition, involves all exit method from * bottom till LCA and execution of transition object function, and * entry methods from LCA till the next state. * * @param smThis Instance Object * @param msg Event that needs to be handled * * @returns * CL_OK on CL_OK (successful transition) <br/> * CL_SM_RC(CL_ERR_NULL_POINTER) on invalid/null instance/msg handle <br/> * SM_ERR_EXIT_FAILED if the exit handler returned failure * CL_SM_RC(CL_ERR_INVALID_STATE) unable to handle event at current state * */ ClRcT clHsmInstanceOnEvent(ClSmInstancePtrT smThis, ClSmEventPtrT msg ) { ClSmStatePtrT curr; ClSmTransitionPtrT tO=0; ClRcT ret = CL_OK; CL_FUNC_ENTER(); CL_ASSERT(smThis); CL_ASSERT(msg); if(!smThis || !smThis->sm || !smThis->current || !msg) { ret = CL_SM_RC(CL_ERR_NULL_POINTER); CL_FUNC_EXIT(); return ret; } for(curr=smThis->current;curr && !tO;) { /* check if the event is in event handler table */ if(msg->eventId < (ClSmEventIdT)curr->maxEventTransitions && msg->eventId >= 0) { tO = curr->eventTransitionTable[msg->eventId].transition; break; } curr=curr->parent; } if(curr && tO) { #ifdef DEBUG clLogTrace(HSM_LOG_AREA,HSM_LOG_CTX_EVENT,"StateMachine [%s] OnEvent [%d] in State [%d:%s]", smThis->name, msg->eventId, curr->type, curr->name); #else clLogTrace(HSM_LOG_AREA,HSM_LOG_CTX_EVENT,"OnEvent %d in state %d", msg->eventId, curr->type); #endif IGNORE_RETURN(_transition(smThis, tO, smThis->current, tO->nextState, msg)); } else { ret = CL_SM_RC(CL_ERR_INVALID_STATE); } CL_FUNC_EXIT(); return ret; }
/** * Add event to the event q. * * API to add event to the state machine instance queue. The * event properties are copied (a new event is created and * the contents of the event passed a re copied to the new * event), but the payload is just referenced and not copied. * * @param smThis Extended State machine Instance handle * @param msg Event information * * @returns * CL_OK on CL_OK <br/> * CL_SM_RC(CL_ERR_NO_MEMORY) on memory allocation FAILURE <br/> * CL_SM_RC(CL_ERR_NULL_POINTER) on invalid/null instance handle <br/> * * @see #clEsmInstanceProcessEvent * @see #clEsmInstanceProcessEvents * */ ClRcT clEsmInstanceEventAdd(ClExSmInstancePtrT smThis, ClSmEventPtrT msg ) { ClRcT ret = CL_OK; CL_FUNC_ENTER(); CL_ASSERT(smThis); CL_ASSERT(msg); if(smThis && msg) { ClSmQueueItemPtrT item; item = (ClSmQueueItemPtrT) mALLOC(sizeof(ClSmQueueItemT)); if(!item) { ret = CL_SM_RC(CL_ERR_NO_MEMORY); } else { if(ESM_LOCK(smThis)!=CL_OK) { ret = SM_ERR_LOCKED; mFREE(item); CL_FUNC_EXIT(); return ret; } item->event = *msg; if (ESM_IS_PAUSED(smThis) && ESM_IS_DROP_ON_PAUSE(smThis)) { ret = CL_OK; mFREE(item); ESM_UNLOCK(smThis); CL_FUNC_EXIT(); return ret; } ret = SMQ_ENQUEUE(smThis->q, item); clLogTrace(ESM_LOG_AREA,ESM_LOG_CTX_EVENT,"Event %d added => ret [%d]", item->event.eventId, ret); ESM_UNLOCK(smThis); } } else { ret = CL_SM_RC(CL_ERR_NULL_POINTER); } CL_FUNC_EXIT(); return ret; }
/** * Continue the extended state machine instance. * * This API to be called if the state machine is paused * and events are just being queued and not being * processed. This API puts the state machine instance * back in regular processing mode. * * @param smThis State Machine Object * * @returns * CL_OK on CL_OK (successful start) <br/> * CL_SM_RC(CL_ERR_NULL_POINTER) on invalid/null instance handle <br/> * * @see #clEsmInstancePause */ ClRcT clEsmInstanceContinue(ClExSmInstancePtrT smThis ) { ClRcT ret = CL_OK; CL_FUNC_ENTER(); CL_ASSERT(smThis); if(smThis && smThis->fsm) { if(!(ESM_IS_PAUSED(smThis))) { ret = SM_ERR_NOT_PAUSED; CL_FUNC_EXIT(); return ret; } #ifdef DEBUG clLogTrace(ESM_LOG_AREA,CL_LOG_CONTEXT_UNSPECIFIED,"Continue [%s]", smThis->fsm->name); #endif ESM_CONTINUE(smThis); } else { ret = CL_SM_RC(CL_ERR_NULL_POINTER); } CL_FUNC_EXIT(); return ret; }
/** * ReStarts the extended state machine instance. * * Please refer to the FSM Restart API. * * @param smThis State Machine Object * * @returns * CL_OK on CL_OK (successful start) <br/> * CL_SM_RC(CL_ERR_NULL_POINTER) on invalid/null instance handle <br/> * * @see #clSmInstanceStart */ ClRcT clEsmInstanceRestart(ClExSmInstancePtrT smThis ) { CL_ASSERT(smThis); if(smThis) { return clSmInstanceRestart(smThis->fsm); } return CL_SM_RC(CL_ERR_NULL_POINTER); }
/** * Process all pending events. * * Handle all pending events in the q. All the events * that are pending in the event q are processed. If * there is an error in the processing, then it returns * back with the error code, even if there are more * events in the q. * * @param smThis Instance Object handle * * @returns * CL_OK on CL_OK <br/> * SM_ERR_NO_EVENT if there are no events in the q <br/> * SM_ERR_LOCKED if the instance is locked <br/> * SM_ERR_PAUSED if the instance is paused <br/> * CL_SM_RC(CL_ERR_NULL_POINTER) on invalid/null instance handle <br/> * * @see #clEsmInstanceProcessEvent */ ClRcT clEsmInstanceProcessEvents(ClExSmInstancePtrT smThis) { ClRcT ret = CL_OK; int k=0; CL_FUNC_ENTER(); CL_ASSERT(smThis); if(smThis) { while(ret == CL_OK) { ret = clEsmInstanceProcessEvent(smThis); if(ret==CL_OK) k++; } /* if more than one event, then return back * status OK */ if(k>0 && ret==SM_ERR_NO_EVENT) { ret = CL_OK; } clLogTrace(ESM_LOG_AREA,ESM_LOG_CTX_EVENT,"Processed %d events",k); } else { ret = CL_SM_RC(CL_ERR_NULL_POINTER); } CL_FUNC_EXIT(); return ret; }
/** * Create a new State machine type. * * API to create a new State macine Type. Maximum states in the * state machine is passed in the parameter. Each state is * identified by a state id (index) and captures the transition * table at each table. * * @param maxStates maximum states in State Machine * @param sm [out] handle to the newly created state machine type * * @returns * CL_OK on CL_OK <br/> * CL_SM_RC(CL_ERR_NO_MEMORY) on memory allocation FAILURE <br/> * CL_SM_RC(CL_ERR_NULL_POINTER) on invalid/null state machine handle <br/> * * @see #clSmTypeDelete */ ClRcT clSmTypeCreate(ClUint16T maxStates, ClSmTemplatePtrT* sm ) { ClRcT ret = CL_OK; int sz = 0; CL_FUNC_ENTER(); CL_ASSERT(sm); CL_DEBUG_PRINT(CL_DEBUG_TRACE, ("Create State Machine Type [%d]", maxStates)); if(sm && (maxStates > 0)) { /* allocate the states and assign to top */ *sm = (ClSmTemplatePtrT) mALLOC(sizeof(ClSmTemplateT)); if(*sm!=0) { sz = sizeof(ClSmStatePtrT)*(int)maxStates; /* allocate space to hold maxStates handles */ (*sm)->top = (ClSmStatePtrT*) mALLOC((size_t)sz); if((*sm)->top) { (*sm)->maxStates = maxStates; (*sm)->objects = 0; /* Introduced a semaphore for handling concurrent * SM Instance creation/deletion */ ret = clOsalMutexCreate( &((*sm)->sem)); if (CL_OK != ret) { mFREE((*sm)->top); mFREE(*sm); ret = SM_ERR_NO_SEMA; } else { /* * default initial state to first state */ (*sm)->init = (*sm)->top[0]; (*sm)->type = SIMPLE_STATE_MACHINE; #ifdef DEBUG (*sm)->name[0] = 0; #endif } } else { mFREE(*sm); } } if(!(*sm) || !(*sm)->top) { ret = CL_SM_RC(CL_ERR_NO_MEMORY); } } else { ret = CL_SM_RC(CL_ERR_NULL_POINTER); } CL_FUNC_EXIT(); return ret; }
/** * Creates a new Extended State machine Instance. * * This API creates a new Extended State macine Instance of given * state machine type. The extended state machine shall include * all the regular state machine instance functionalities, plus * additional event queue, history, and lock capabilities. * * @param sm State machine type * @param instance [out] newly created extended state machine instance * * @returns * CL_OK on CL_OK <br/> * CL_SM_RC(CL_ERR_NO_MEMORY) on memory allocation FAILURE <br/> * CL_SM_RC(CL_ERR_NULL_POINTER) on invalid/null sm / instance <br/> * * @see #clEsmInstanceDelete */ ClRcT clEsmInstanceCreate(ClSmTemplatePtrT sm, ClExSmInstancePtrT* instance ) { ClRcT ret = CL_OK; CL_FUNC_ENTER(); CL_ASSERT(instance); CL_ASSERT(sm); clLogTrace(ESM_LOG_AREA,ESM_LOG_CTX_CREATE,"Create Extended State Machine Instance"); if(sm && instance) { /* allocate the instance space */ *instance = (ClExSmInstancePtrT) mALLOC(sizeof(ClExSmInstanceT)); if(*instance!=0) { memset(*instance, 0, sizeof(ClExSmInstanceT)); /* call sm create here */ ret = clSmInstanceCreate(sm, &(*instance)->fsm); if(ret == CL_OK) { ret = clOsalMutexCreate(&(*instance)->lock); if (CL_OK != ret) { clSmInstanceDelete((*instance)->fsm); mFREE(*instance); ret = SM_ERR_NO_SEMA; } else { /* create queue and init */ ret = SMQ_CREATE((*instance)->q); if(ret == CL_OK) { /* init log buffer */ ESM_LOG_INIT((*instance)->log, ESM_LOG_ENTRIES); } if(!(*instance)->log.buffer || ret != CL_OK) { /* delete the instance */ ret = clSmInstanceDelete((*instance)->fsm); /* delete the mutex */ clOsalMutexDelete((*instance)->lock); /* check if q init succeeded */ if(ret == CL_OK) { /* delete the queue */ clQueueDelete(&((*instance)->q)); } /* free the instance */ mFREE(*instance); ret = CL_SM_RC(CL_ERR_NO_MEMORY); } } } } else { ret = CL_SM_RC(CL_ERR_NO_MEMORY); } } else { ret = CL_SM_RC(CL_ERR_NULL_POINTER); } CL_FUNC_EXIT(); return ret; }
/** * Event Handling Function. * * Removes the first event from the q and processes it. Apart * from the event handling thats done at the SMType (Simple * State machine), this API also handles like if this state * machine instance is locked / not. Also, checks if its in * history state and if so, then returns back to the previous * state. * * @param smThis Instance Object * * @returns * CL_OK on CL_OK <br/> * SM_ERR_NO_EVENT if there are no events in the q <br/> * SM_ERR_LOCKED if the instance is locked <br/> * SM_ERR_PAUSED if the instance is paused <br/> * CL_SM_RC(CL_ERR_NULL_POINTER) on invalid/null instance handle <br/> * * @see #clEsmInstanceProcessEvents */ ClRcT clEsmInstanceProcessEvent(ClExSmInstancePtrT smThis) { ClRcT ret = CL_OK; ClSmEventPtrT msg=0; ClSmLogInfoPtrT logBuf; CL_FUNC_ENTER(); CL_ASSERT(smThis); if(smThis && smThis->fsm && smThis->fsm->sm && smThis->fsm->current) { ClUint32T sz; if(ESM_LOCK(smThis)!=CL_OK) { ret = SM_ERR_LOCKED; CL_FUNC_EXIT(); return ret; } SMQ_SIZE(smThis->q, sz); if(sz == 0) { ret = SM_ERR_NO_EVENT; } else if(ESM_IS_PAUSED(smThis)) { ret = SM_ERR_PAUSED; } else if (ESM_IS_BUSY(smThis)) { ret = SM_ERR_BUSY; } else { ClSmQueueItemPtrT item; ClRcT rc; /* dequeue the message */ rc = SMQ_DEQUEUE(smThis->q, item); if(rc!=CL_OK || !item) { ret = CL_SM_RC(CL_ERR_NULL_POINTER); } else { ClSmStatePtrT history = smThis->fsm->current; ClSmTransitionPtrT trans = 0; ESM_SET_BUSY_STATE(smThis); ESM_UNLOCK(smThis); msg = &item->event; /* if its in history state, then * need to take care of the next state * (use previous state, if not configured) */ if(history->type == ESM_HISTORY_STATE) { if(history->maxEventTransitions >(ClUint16T) msg->eventId && msg->eventId >= 0 && history->eventTransitionTable[msg->eventId].transition) { trans = history->eventTransitionTable[msg->eventId].transition; if(!trans->nextState) { /* This is not such a good idea, what happens another * instance uses the type, it will reject it as if though * there is a predefined next state and will not go to * history state */ trans->nextState = smThis->previous; clLogTrace(ESM_LOG_AREA,ESM_LOG_CTX_EVENT,"History State Set as Next State!"); } else { trans = 0; } } else { clLogTrace(ESM_LOG_AREA,ESM_LOG_CTX_EVENT,"Unknown Event in History State"); } } #ifdef DEBUG clLogTrace(ESM_LOG_AREA,ESM_LOG_CTX_EVENT,"StateMachine [%s] OnEvent [%d]", smThis->fsm->name, msg->eventId); #else clLogTrace(ESM_LOG_AREA,ESM_LOG_CTX_EVENT,"OnEvent %d", msg->eventId); #endif ret = clSmInstanceOnEvent(smThis->fsm, msg); if(ret == CL_OK) { /* update the history state */ smThis->previous = history; /* record log */ logBuf = ESM_LOG_BUF(smThis->log); logBuf->eventId = msg->eventId; logBuf->from = smThis->previous; logBuf->to = smThis->fsm->current; ESM_LOG_IDX_INCR(smThis->log); } /* restore the original state machine, if history * state is set */ if(trans) { trans->nextState = 0; } /* free the dequeued item */ mFREE(item); ESM_LOCK(smThis); ESM_SET_IDL_STATE(smThis); } } ESM_UNLOCK(smThis); } else { ret = CL_SM_RC(CL_ERR_NULL_POINTER); } CL_FUNC_EXIT(); return ret; }
/** * Delete Extended State machine Instance. * * API to delete a previously created State macine Instance. Also * frees up the events that are in the Q. * * @param smThis Extended State machine Instance to be deleted * * @returns * CL_OK on CL_OK <br/> * CL_SM_RC(CL_ERR_NULL_POINTER) on invalid/null instance handle <br/> * * @see #clEsmInstanceCreate * */ ClRcT clEsmInstanceDelete(ClExSmInstancePtrT smThis ) { ClRcT ret = CL_OK; CL_FUNC_ENTER(); CL_ASSERT(smThis); clLogTrace(ESM_LOG_AREA,ESM_LOG_CTX_DELETE,"Delete Extended State Machine Instance"); if(smThis) { ClUint32T sz = 0; if(ESM_LOCK(smThis)!=CL_OK) { ret = SM_ERR_LOCKED; CL_FUNC_EXIT(); return ret; } /* free the fsm first */ ret = clSmInstanceDelete(smThis->fsm); SMQ_SIZE(smThis->q, sz); /* Check if the queue is empty, if not, dequeue and delete them */ if(sz > 0) { ClSmQueueItemPtrT item; ClRcT rc; rc = SMQ_DEQUEUE(smThis->q, item); while(rc==CL_OK && item) { mFREE(item); rc = SMQ_DEQUEUE(smThis->q, item); } clLogInfo(ESM_LOG_AREA,ESM_LOG_CTX_DELETE,"***Delete: Events are present in Q! Dropped to floor!!! ***"); } /* delete the queue */ clQueueDelete(&smThis->q); /* free the history buffer */ mFREE(smThis->log.buffer); /* unlock it before, so we can delete the mutex */ ESM_UNLOCK(smThis); /* delete the mutex */ clOsalMutexDelete(smThis->lock); /* free the object */ mFREE(smThis); } else { ret = CL_SM_RC(CL_ERR_NULL_POINTER); } CL_FUNC_EXIT(); return ret; }