This library is designed to create and run state graphs. It supports hierarchical states and parallel states execution.
Diff: StateMachineLib/source/StateMachine.cpp
- 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; +}