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

Revision:
0:f4fdca2c4c67
Child:
2:5e2336b52d0a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StateMachineLib/source/StateMachine.cpp	Tue Oct 03 08:29:22 2017 +0000
@@ -0,0 +1,259 @@
+////////////////////////////////////////////////////////////////////////////////
+// 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;
+}