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

Committer:
martin13
Date:
Tue Feb 02 20:47:45 2021 +0000
Revision:
4:22b4462fcb26
Parent:
2:5e2336b52d0a
Added unittests

Who changed what in which revision?

UserRevisionLine numberNew contents of line
martin13 0:f4fdca2c4c67 1 ////////////////////////////////////////////////////////////////////////////////
martin13 0:f4fdca2c4c67 2 // Copyright Rottor SAS 2017
martin13 0:f4fdca2c4c67 3 // All rigths reserved.
martin13 0:f4fdca2c4c67 4 //
martin13 0:f4fdca2c4c67 5 // File Name : StateMachine.cpp
martin13 0:f4fdca2c4c67 6 // Authors : Martin Matignon
martin13 0:f4fdca2c4c67 7 //
martin13 0:f4fdca2c4c67 8 // If you find any bug or if you have any question please contact
martin13 0:f4fdca2c4c67 9 // Martin Matignon <martin.matignon@rottor.fr>
martin13 0:f4fdca2c4c67 10 // Nicolas Forestier <nicolas.forestier@rottor.fr>
martin13 0:f4fdca2c4c67 11 //
martin13 0:f4fdca2c4c67 12 ////////////////////////////////////////////////////////////////////////////////
martin13 0:f4fdca2c4c67 13
martin13 0:f4fdca2c4c67 14 #include "StateMachine.h"
martin13 0:f4fdca2c4c67 15 #include "Logger.h"
martin13 0:f4fdca2c4c67 16
martin13 0:f4fdca2c4c67 17 StateMachine::StateItem_t::StateItem_t(State* _state){
martin13 0:f4fdca2c4c67 18 state = _state;
martin13 0:f4fdca2c4c67 19 transitions = new Transitions();
martin13 0:f4fdca2c4c67 20 next = NULL;
martin13 0:f4fdca2c4c67 21 }
martin13 0:f4fdca2c4c67 22
martin13 0:f4fdca2c4c67 23
martin13 0:f4fdca2c4c67 24 StateMachine::StateMachine(const char* stateMachineUUID, UserData *ud):
martin13 0:f4fdca2c4c67 25 State(stateMachineUUID, STATE_MACHINE),
martin13 0:f4fdca2c4c67 26 m_stateList(NULL),
martin13 0:f4fdca2c4c67 27 m_nbStates(0),
martin13 0:f4fdca2c4c67 28 m_currentItem(NULL),
martin13 0:f4fdca2c4c67 29 m_initalStateUUID(NULL),
martin13 0:f4fdca2c4c67 30 m_targetStateUUID(NULL),
martin13 0:f4fdca2c4c67 31 m_userData(ud)
martin13 0:f4fdca2c4c67 32 {
martin13 0:f4fdca2c4c67 33 /* Empty */
martin13 0:f4fdca2c4c67 34 }
martin13 0:f4fdca2c4c67 35
martin13 0:f4fdca2c4c67 36 void StateMachine::setInitialState(State* state){
martin13 0:f4fdca2c4c67 37 m_initalStateUUID = state->getUUID();
martin13 0:f4fdca2c4c67 38 m_targetStateUUID = m_initalStateUUID;
martin13 0:f4fdca2c4c67 39 }
martin13 0:f4fdca2c4c67 40
martin13 0:f4fdca2c4c67 41 void StateMachine::connect(State* state, const char* outcome, State* target){
martin13 0:f4fdca2c4c67 42
martin13 0:f4fdca2c4c67 43 StateItem_t *item = getItemByUUID(state->getUUID());
martin13 0:f4fdca2c4c67 44
martin13 0:f4fdca2c4c67 45 Transitions::Transition_t* newT = new Transitions::Transition_t(outcome, target, NULL);
martin13 0:f4fdca2c4c67 46
martin13 0:f4fdca2c4c67 47 item->transitions->addTransition(newT);
martin13 0:f4fdca2c4c67 48 }
martin13 0:f4fdca2c4c67 49
martin13 0:f4fdca2c4c67 50 void StateMachine::remap(State* state, const char* outcome, State* target){
martin13 0:f4fdca2c4c67 51
martin13 0:f4fdca2c4c67 52 StateItem_t *item = getItemByUUID(state->getUUID());
martin13 0:f4fdca2c4c67 53
martin13 0:f4fdca2c4c67 54 if (item != NULL){
martin13 0:f4fdca2c4c67 55
martin13 0:f4fdca2c4c67 56 Transitions::Transition_t* trans = item->transitions->getTransition(outcome);
martin13 0:f4fdca2c4c67 57
martin13 0:f4fdca2c4c67 58 if (trans != NULL){
martin13 0:f4fdca2c4c67 59 trans->target = target;
martin13 0:f4fdca2c4c67 60 trans->_exit_ = NULL;
martin13 0:f4fdca2c4c67 61 }
martin13 0:f4fdca2c4c67 62 }
martin13 0:f4fdca2c4c67 63 }
martin13 0:f4fdca2c4c67 64
martin13 0:f4fdca2c4c67 65 void StateMachine::connect(State* state, const char* outcome, const char* _exit){
martin13 0:f4fdca2c4c67 66
martin13 0:f4fdca2c4c67 67 StateItem_t *item = getItemByUUID(state->getUUID());
martin13 0:f4fdca2c4c67 68
martin13 0:f4fdca2c4c67 69 Transitions::Transition_t* newT = new Transitions::Transition_t(outcome, NULL, _exit);
martin13 0:f4fdca2c4c67 70
martin13 0:f4fdca2c4c67 71 item->transitions->addTransition(newT);
martin13 0:f4fdca2c4c67 72 }
martin13 0:f4fdca2c4c67 73
martin13 0:f4fdca2c4c67 74 void StateMachine::remap(State* state, const char* outcome, const char* _exit){
martin13 0:f4fdca2c4c67 75
martin13 0:f4fdca2c4c67 76 StateItem_t *item = getItemByUUID(state->getUUID());
martin13 0:f4fdca2c4c67 77
martin13 0:f4fdca2c4c67 78 if (item != NULL){
martin13 0:f4fdca2c4c67 79
martin13 0:f4fdca2c4c67 80 Transitions::Transition_t* trans = item->transitions->getTransition(outcome);
martin13 0:f4fdca2c4c67 81
martin13 0:f4fdca2c4c67 82 if (trans != NULL){
martin13 0:f4fdca2c4c67 83 trans->target = NULL;
martin13 0:f4fdca2c4c67 84 trans->_exit_ = _exit;
martin13 0:f4fdca2c4c67 85 }
martin13 0:f4fdca2c4c67 86 }
martin13 0:f4fdca2c4c67 87 }
martin13 0:f4fdca2c4c67 88
martin13 0:f4fdca2c4c67 89 void StateMachine::_addState(State *state){
martin13 0:f4fdca2c4c67 90
martin13 0:f4fdca2c4c67 91 StateItem_t *newItem = new StateItem_t(state);
martin13 0:f4fdca2c4c67 92
martin13 0:f4fdca2c4c67 93 if(m_stateList == NULL){
martin13 0:f4fdca2c4c67 94
martin13 0:f4fdca2c4c67 95 m_stateList = newItem;
martin13 0:f4fdca2c4c67 96
martin13 0:f4fdca2c4c67 97 } else {
martin13 0:f4fdca2c4c67 98
martin13 0:f4fdca2c4c67 99 StateItem_t *temp = m_stateList;
martin13 0:f4fdca2c4c67 100
martin13 0:f4fdca2c4c67 101 while(temp->next != NULL)
martin13 0:f4fdca2c4c67 102 {
martin13 0:f4fdca2c4c67 103 temp = temp->next;
martin13 0:f4fdca2c4c67 104 }
martin13 0:f4fdca2c4c67 105 temp->next = newItem;
martin13 0:f4fdca2c4c67 106 }
martin13 0:f4fdca2c4c67 107
martin13 0:f4fdca2c4c67 108 m_nbStates++;
martin13 0:f4fdca2c4c67 109 }
martin13 0:f4fdca2c4c67 110
martin13 0:f4fdca2c4c67 111 void StateMachine::onEntry(){
martin13 0:f4fdca2c4c67 112 /* Empty */
martin13 0:f4fdca2c4c67 113 };
martin13 0:f4fdca2c4c67 114
martin13 0:f4fdca2c4c67 115 const char* StateMachine::onExecute(){
martin13 0:f4fdca2c4c67 116
martin13 0:f4fdca2c4c67 117 Logger::debug("IN STATE(%s)",m_targetStateUUID);
martin13 0:f4fdca2c4c67 118
martin13 0:f4fdca2c4c67 119 // Find the target item state
martin13 0:f4fdca2c4c67 120 m_currentItem = getItemByUUID(m_targetStateUUID);
martin13 0:f4fdca2c4c67 121
martin13 0:f4fdca2c4c67 122 // Target state execution
martin13 0:f4fdca2c4c67 123 m_currentItem->state->onEntry();
martin13 0:f4fdca2c4c67 124 const char *outcome = m_currentItem->state->onExecute();
martin13 0:f4fdca2c4c67 125 m_currentItem->state->onExit();
martin13 0:f4fdca2c4c67 126
martin13 0:f4fdca2c4c67 127 // Check if state is not preempted
martin13 0:f4fdca2c4c67 128 if (isPreempted() || strcmp(outcome, PREEMPTED) == 0)
martin13 0:f4fdca2c4c67 129 {
martin13 0:f4fdca2c4c67 130
martin13 0:f4fdca2c4c67 131 Logger::debug("{ STATE(%s) : { STATE(%s) : { OUTCOME(PREEMPTED) : EXIT(PREEMPTED) } } }",
martin13 0:f4fdca2c4c67 132 getUUID(),
martin13 0:f4fdca2c4c67 133 m_currentItem->state->getUUID());
martin13 0:f4fdca2c4c67 134
martin13 0:f4fdca2c4c67 135 return PREEMPTED;
martin13 0:f4fdca2c4c67 136 }
martin13 0:f4fdca2c4c67 137
martin13 0:f4fdca2c4c67 138 // Find transition attached to outcome
martin13 0:f4fdca2c4c67 139 Transitions::Transition_t* transition = m_currentItem->transitions->getTransition(outcome);
martin13 0:f4fdca2c4c67 140
martin13 0:f4fdca2c4c67 141 if (transition != NULL)
martin13 0:f4fdca2c4c67 142 {
martin13 0:f4fdca2c4c67 143 if (transition->target == NULL)
martin13 0:f4fdca2c4c67 144 {
martin13 0:f4fdca2c4c67 145 if(transition->_exit_ != NULL)
martin13 0:f4fdca2c4c67 146 {
martin13 0:f4fdca2c4c67 147 if (strcmp(outcome, transition->outcome) == 0)
martin13 0:f4fdca2c4c67 148 {
martin13 0:f4fdca2c4c67 149 Logger::debug("{ STATE(%s) : { STATE(%s) : { OUTCOME(%s) : EXIT(%s) } } }",
martin13 0:f4fdca2c4c67 150 getUUID(),
martin13 0:f4fdca2c4c67 151 m_currentItem->state->getUUID(),
martin13 0:f4fdca2c4c67 152 outcome,
martin13 0:f4fdca2c4c67 153 transition->_exit_);
martin13 0:f4fdca2c4c67 154
martin13 0:f4fdca2c4c67 155 // Capture the inital target state uuid
martin13 0:f4fdca2c4c67 156 m_targetStateUUID = m_initalStateUUID;
martin13 0:f4fdca2c4c67 157
martin13 0:f4fdca2c4c67 158 // Exit state machine with _exit_ outcome
martin13 0:f4fdca2c4c67 159 return transition->_exit_;
martin13 0:f4fdca2c4c67 160 }
martin13 0:f4fdca2c4c67 161 } else{
martin13 0:f4fdca2c4c67 162 Logger::err("NO EXIT OUTCOME FOUND FROM STATE(%s)");
martin13 0:f4fdca2c4c67 163 }
martin13 0:f4fdca2c4c67 164 }
martin13 0:f4fdca2c4c67 165 else{
martin13 0:f4fdca2c4c67 166
martin13 0:f4fdca2c4c67 167 // Capture the next target state uuid
martin13 0:f4fdca2c4c67 168 m_targetStateUUID = transition->target->getUUID();
martin13 0:f4fdca2c4c67 169
martin13 0:f4fdca2c4c67 170 Logger::debug("{ STATE(%s) : { STATE(%s) : { OUTCOME(%s) : STATE(%s) } } }",
martin13 0:f4fdca2c4c67 171 getUUID(),
martin13 0:f4fdca2c4c67 172 m_currentItem->state->getUUID(),
martin13 0:f4fdca2c4c67 173 outcome,
martin13 0:f4fdca2c4c67 174 m_targetStateUUID);
martin13 0:f4fdca2c4c67 175
martin13 2:5e2336b52d0a 176 // Recall execution for sheduling the next target state
martin13 0:f4fdca2c4c67 177 return this->onExecute();
martin13 0:f4fdca2c4c67 178 }
martin13 0:f4fdca2c4c67 179 }
martin13 0:f4fdca2c4c67 180
martin13 0:f4fdca2c4c67 181 Logger::err("{ STATE(%s) : { STATE(%s) : { OUTCOME(%s) : STATE(NULL) } } } !!! OUTCOME \"%s::%s\" NO MAPPED !!!",
martin13 0:f4fdca2c4c67 182 getUUID(), m_currentItem->state->getUUID(),
martin13 0:f4fdca2c4c67 183 outcome, m_currentItem->state->getUUID(), outcome);
martin13 0:f4fdca2c4c67 184
martin13 0:f4fdca2c4c67 185 // Capture the inital target state uuid
martin13 0:f4fdca2c4c67 186 m_targetStateUUID = m_initalStateUUID;
martin13 0:f4fdca2c4c67 187
martin13 0:f4fdca2c4c67 188 // State machine finished
martin13 0:f4fdca2c4c67 189 return outcome;
martin13 0:f4fdca2c4c67 190 }
martin13 0:f4fdca2c4c67 191
martin13 0:f4fdca2c4c67 192 void StateMachine::onExit(){
martin13 0:f4fdca2c4c67 193 /* Empty */
martin13 0:f4fdca2c4c67 194 }
martin13 0:f4fdca2c4c67 195
martin13 0:f4fdca2c4c67 196 const char* StateMachine::execute(){
martin13 0:f4fdca2c4c67 197
martin13 0:f4fdca2c4c67 198 this->onEntry();
martin13 0:f4fdca2c4c67 199
martin13 0:f4fdca2c4c67 200 const char* outcome = this->onExecute();
martin13 0:f4fdca2c4c67 201
martin13 0:f4fdca2c4c67 202 this->onExit();
martin13 0:f4fdca2c4c67 203
martin13 0:f4fdca2c4c67 204 if (preempt_flag){ preempt_flag = false; }
martin13 0:f4fdca2c4c67 205
martin13 0:f4fdca2c4c67 206 return outcome;
martin13 0:f4fdca2c4c67 207 }
martin13 0:f4fdca2c4c67 208
martin13 0:f4fdca2c4c67 209 StateMachine::StateItem_t* StateMachine::getStatesList(){
martin13 0:f4fdca2c4c67 210 return m_stateList;
martin13 0:f4fdca2c4c67 211 }
martin13 0:f4fdca2c4c67 212
martin13 0:f4fdca2c4c67 213 int StateMachine::getNumberStates(){
martin13 0:f4fdca2c4c67 214 return m_nbStates;
martin13 0:f4fdca2c4c67 215 }
martin13 0:f4fdca2c4c67 216
martin13 0:f4fdca2c4c67 217 UserData* StateMachine::getUserData(){
martin13 0:f4fdca2c4c67 218 return m_userData;
martin13 0:f4fdca2c4c67 219 }
martin13 0:f4fdca2c4c67 220
martin13 0:f4fdca2c4c67 221 void StateMachine::printGraph(string level){
martin13 0:f4fdca2c4c67 222
martin13 0:f4fdca2c4c67 223 StateItem_t *item = m_stateList;
martin13 0:f4fdca2c4c67 224
martin13 0:f4fdca2c4c67 225 Logger::debug("%s- STATE MACHINE \"%s\":",level.c_str(),getUUID());
martin13 0:f4fdca2c4c67 226
martin13 0:f4fdca2c4c67 227 level += " ";
martin13 0:f4fdca2c4c67 228
martin13 0:f4fdca2c4c67 229 while(item != NULL)
martin13 0:f4fdca2c4c67 230 {
martin13 0:f4fdca2c4c67 231 if (item->state->getType() == STATE_MACHINE){
martin13 0:f4fdca2c4c67 232 ((StateMachine *)item->state)->printGraph(level);
martin13 0:f4fdca2c4c67 233 }
martin13 0:f4fdca2c4c67 234 else{
martin13 0:f4fdca2c4c67 235 Logger::debug("%s- STATE \"%s\"",level.c_str(), item->state->getUUID());
martin13 0:f4fdca2c4c67 236 }
martin13 0:f4fdca2c4c67 237
martin13 0:f4fdca2c4c67 238 if (item->transitions != NULL){
martin13 0:f4fdca2c4c67 239 item->transitions->printList(level);
martin13 0:f4fdca2c4c67 240 }
martin13 0:f4fdca2c4c67 241
martin13 0:f4fdca2c4c67 242 item = item->next;
martin13 0:f4fdca2c4c67 243 }
martin13 0:f4fdca2c4c67 244 }
martin13 0:f4fdca2c4c67 245
martin13 0:f4fdca2c4c67 246 StateMachine::StateItem_t* StateMachine::getItemByUUID(const char* uuid){
martin13 0:f4fdca2c4c67 247
martin13 0:f4fdca2c4c67 248 StateItem_t *list = m_stateList;
martin13 0:f4fdca2c4c67 249
martin13 0:f4fdca2c4c67 250 while(list != NULL)
martin13 0:f4fdca2c4c67 251 {
martin13 0:f4fdca2c4c67 252 if (strcmp(list->state->getUUID(), uuid) == 0){
martin13 0:f4fdca2c4c67 253 return list;
martin13 0:f4fdca2c4c67 254 }
martin13 0:f4fdca2c4c67 255 list = list->next;
martin13 0:f4fdca2c4c67 256 }
martin13 0:f4fdca2c4c67 257
martin13 0:f4fdca2c4c67 258 return NULL;
martin13 0:f4fdca2c4c67 259 }