This library is designed to create and run state graphs. It supports hierarchical states and parallel states execution.

StateMachineLib/source/StateMachine.cpp

Committer:
martin13
Date:
2017-10-03
Revision:
0:f4fdca2c4c67
Child:
2:5e2336b52d0a

File content as of revision 0:f4fdca2c4c67:

////////////////////////////////////////////////////////////////////////////////
// Copyright Rottor SAS 2017
// All rigths reserved.
//
// File Name : StateMachine.cpp
// Authors : Martin Matignon
//
// If you find any bug or if you have any question please contact
// Martin Matignon <martin.matignon@rottor.fr>
// Nicolas Forestier <nicolas.forestier@rottor.fr>
//
////////////////////////////////////////////////////////////////////////////////

#include "StateMachine.h"
#include "Logger.h"
 
StateMachine::StateItem_t::StateItem_t(State* _state){
    state       = _state;
    transitions = new Transitions();
    next        = NULL;
}


StateMachine::StateMachine(const char* stateMachineUUID, UserData *ud):
  State(stateMachineUUID, STATE_MACHINE),
  m_stateList(NULL),
  m_nbStates(0),
  m_currentItem(NULL),
  m_initalStateUUID(NULL),
  m_targetStateUUID(NULL),
  m_userData(ud)
{
    /* Empty */
}

void StateMachine::setInitialState(State* state){
    m_initalStateUUID = state->getUUID();
    m_targetStateUUID = m_initalStateUUID;
}

void StateMachine::connect(State* state, const char* outcome, State* target){
    
    StateItem_t *item = getItemByUUID(state->getUUID());
    
    Transitions::Transition_t* newT = new Transitions::Transition_t(outcome, target, NULL);
    
    item->transitions->addTransition(newT);
}

void StateMachine::remap(State* state, const char* outcome, State* target){
    
    StateItem_t *item = getItemByUUID(state->getUUID());
    
    if (item != NULL){
    
        Transitions::Transition_t* trans = item->transitions->getTransition(outcome);
        
        if (trans != NULL){
            trans->target = target;
            trans->_exit_ = NULL;
        }
    }
}

void StateMachine::connect(State* state, const char* outcome, const char* _exit){
    
    StateItem_t *item = getItemByUUID(state->getUUID());
    
    Transitions::Transition_t* newT = new Transitions::Transition_t(outcome, NULL, _exit);
    
    item->transitions->addTransition(newT);  
}

void StateMachine::remap(State* state, const char* outcome, const char* _exit){

    StateItem_t *item = getItemByUUID(state->getUUID());
    
    if (item != NULL){
    
        Transitions::Transition_t* trans = item->transitions->getTransition(outcome);
        
        if (trans != NULL){
            trans->target = NULL;
            trans->_exit_ = _exit;
        }
    } 
}

void StateMachine::_addState(State *state){
    
    StateItem_t *newItem = new StateItem_t(state);
    
    if(m_stateList == NULL){
        
        m_stateList = newItem;
        
    } else {
        
        StateItem_t *temp = m_stateList;

        while(temp->next != NULL)
        {
            temp = temp->next;
        }
        temp->next = newItem;
    }
    
    m_nbStates++;
}

void StateMachine::onEntry(){
    /* Empty */
};

const char* StateMachine::onExecute(){
    
    Logger::debug("IN STATE(%s)",m_targetStateUUID);
    
    // Find the target item state
    m_currentItem = getItemByUUID(m_targetStateUUID);
    
    // Target state execution
    m_currentItem->state->onEntry();
    const char *outcome = m_currentItem->state->onExecute();
    m_currentItem->state->onExit();
    
    // Check if state is not preempted
    if (isPreempted() || strcmp(outcome, PREEMPTED) == 0)
    {
        
        Logger::debug("{ STATE(%s) : { STATE(%s) : { OUTCOME(PREEMPTED) : EXIT(PREEMPTED) } } }",
                      getUUID(),
                      m_currentItem->state->getUUID());
        
        return PREEMPTED;
    }
    
    // Find transition attached to outcome
    Transitions::Transition_t* transition = m_currentItem->transitions->getTransition(outcome);
    
    if (transition != NULL)
    {
        if (transition->target == NULL)
        {   
            if(transition->_exit_ != NULL)
            {
                if (strcmp(outcome, transition->outcome) == 0)
                {
                    Logger::debug("{ STATE(%s) : { STATE(%s) : { OUTCOME(%s) : EXIT(%s) } } }",
                                  getUUID(),
                                  m_currentItem->state->getUUID(),
                                  outcome,
                                  transition->_exit_);
                    
                    // Capture the inital target state uuid
                    m_targetStateUUID = m_initalStateUUID;
                    
                    // Exit state machine with _exit_ outcome
                    return transition->_exit_;
                }
            } else{
                Logger::err("NO EXIT OUTCOME FOUND FROM STATE(%s)");    
            }
        }
        else{
            
            // Capture the next target state uuid
            m_targetStateUUID = transition->target->getUUID();
            
            Logger::debug("{ STATE(%s) : { STATE(%s) : { OUTCOME(%s) : STATE(%s) } } }",
                          getUUID(),
                          m_currentItem->state->getUUID(),
                          outcome,
                          m_targetStateUUID);
            
            // Recall execution with the next target state
            return this->onExecute();
        }
    }
           
    Logger::err("{ STATE(%s) : { STATE(%s) : { OUTCOME(%s) : STATE(NULL) } } } !!! OUTCOME \"%s::%s\" NO MAPPED !!!",
                getUUID(), m_currentItem->state->getUUID(),
                outcome, m_currentItem->state->getUUID(), outcome);
    
    // Capture the inital target state uuid
    m_targetStateUUID = m_initalStateUUID;
    
    // State machine finished
    return outcome;
}

void StateMachine::onExit(){
    /* Empty */
}

const char* StateMachine::execute(){
    
    this->onEntry();
    
    const char* outcome = this->onExecute();
    
    this->onExit();
    
    if (preempt_flag){ preempt_flag = false; }
    
    return outcome;
}

StateMachine::StateItem_t* StateMachine::getStatesList(){
    return m_stateList;
}

int StateMachine::getNumberStates(){
    return m_nbStates;    
}

UserData* StateMachine::getUserData(){
    return m_userData;   
}

void StateMachine::printGraph(string level){
    
    StateItem_t *item = m_stateList;
    
    Logger::debug("%s- STATE MACHINE \"%s\":",level.c_str(),getUUID()); 
    
    level += "    ";

    while(item != NULL)
    {
        if (item->state->getType() == STATE_MACHINE){
            ((StateMachine *)item->state)->printGraph(level);
        }
        else{
            Logger::debug("%s- STATE \"%s\"",level.c_str(), item->state->getUUID());
        }
        
        if (item->transitions != NULL){
            item->transitions->printList(level);
        }   
        
        item = item->next;
    } 
}

StateMachine::StateItem_t* StateMachine::getItemByUUID(const char* uuid){
    
    StateItem_t *list = m_stateList;

    while(list != NULL)
    {
        if (strcmp(list->state->getUUID(), uuid) == 0){
            return list;
        }
        list = list->next;
    }
    
    return NULL;
}