示例#1
0
/**
 *  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;
}