State

The state machine components are defined in theState.hheader file. It declares three classes: State, State Machine and Transition. A subclass ofStatecan implement the following methods:

  • Entry(): implement the entry action of the state;
  • During(): implement the during action of the state;
  • Exit(): implement the exit action of the state;
  • Initial(): return the index of the initial substate.

The transitions of the state are stored in the variabletransitions. The code for State and Transition class is as follows:

//State.h

#ifndef ROBOCALC_STATE_H_
#define ROBOCALC_STATE_H_

#include <vector>
#include <memory>

#define STATE_DEBUG

namespace robochart {
class Transition;
class State {

public:
    std::string name;
    bool mark;
    State(std::string n) : name(n), stage(s_Inactive), mark(false) {}
    virtual ~State() { printf("Deleting state %s\n", name.c_str()); }
    virtual void Entry() {}
    virtual void During() {}
    virtual void Exit() {}
    virtual int Initial() { return -1; }

    enum Stages {
        s_Enter, s_Execute, s_Exit, s_Inactive
    };
    Stages stage;
    std::vector<std::shared_ptr<State>> states;
    std::vector<std::shared_ptr<Transition>> transitions;
    virtual void Execute();

    bool TryTransitions();

    bool TryExecuteSubstates(std::vector<std::shared_ptr<State>> s);

    void CancelTransitions(int i);
};

class StateMachine: public State {
public:
    StateMachine(std::string n): State(n) {}
    virtual ~StateMachine() {}
};

class Transition {

private:
    std::weak_ptr<State> source, target;
public:
    std::string name;
public:
    Transition(std::string n, std::weak_ptr<State> src, std::weak_ptr<State> tgt) :
            name(n), source(src), target(tgt) {
    }
    virtual void Reg() {}
    virtual bool Check() { return true; }
    virtual void Cancel() {}
    virtual bool Condition() { return true; }
    virtual void Action() {}
    virtual void ClearEvent() {};
    virtual ~Transition() { source.reset(); target.reset(); printf("Deleting transition\n");}
    bool Execute();
};

}

#endif

If the state is composite, its substates are stored in the variablestates. In this case, the functionInitial()must be implemented and return the index of the initial state. Any state (e.g. a state machine) must extend the class State and can provide entry, during and exit actions.

Notice that it is not possible to define the transitions of the state directly in the class because of a circular dependency between states and transitions. For this reason, transitions must be instantiated and added to the variabletransitionsof the source state.

For example, in our obstacle avoidance example, theTurningstate has reference to the robot (to access theMoveoperation), and the state machine (to access variable dir and clock T); it provides the entry action that sets the angular speed of the robot.

State Machine

A state machine is just a state. This class exists only to make the notion of a state machine explicit. To execute a state machine, the execute function is called. Inside this function, the functions try_execute_substates, try_transitions, cancel_transitions are called. A state has three stages: s_Enter, s_Execute and s_Exit.

//State.cpp
#include "State.h"

namespace robochart {

bool Transition::Execute() {
    if (Condition() & Check()) {  //check condition() first if it is false no need to perform check(); condition() && check()
        auto src = source.lock();
        src->stage = State::s_Exit;
        src->Execute();
        Action();
        auto tgt = target.lock();
        tgt->stage = State::s_Enter;
        tgt->Execute();
        return true;
    }
    return false;
}

void State::Execute() {
    switch (stage) {
    case s_Enter:
#ifdef STATE_DEBUG
        printf("Entering State %s\n", this->name.c_str());
#endif
        Entry();
        if (Initial() >= 0) {
            states[Initial()]->stage = s_Enter;  //this has already makes sure that every time the state machine is entered, it starts executing from initial state?
            states[Initial()]->Execute();
        }
        stage = s_Execute;
        break;
    case s_Execute:
#ifdef STATE_DEBUG
        printf("Executing a state %s\n", this->name.c_str());
#endif
        while(TryExecuteSubstates(states));      //this makes sure more than one transition can happen at one cycle; execute the state from bottom to up
        if (TryTransitions() == false) {
#ifdef STATE_DEBUG
            printf("Executing during action of %s!\n", this->name.c_str());
#endif
            During();                              //if no transition is enabled, execute during action in every time step
        }
        else {
#ifdef STATE_DEBUG
            printf("Not Executing during action of %s!\n", this->name.c_str());
#endif
        }
        break;
    case s_Exit:
        Exit();
        stage = s_Inactive;
        break;
    }
}

bool State::TryTransitions() {
#ifdef STATE_DEBUG
    printf("trying %ld transitions\n", transitions.size());
#endif
    for (int i = 0; i < transitions.size(); i++) {
#ifdef STATE_DEBUG
        printf("trying transition: %s\n", transitions[i]->name.c_str());
#endif
        bool b = transitions[i]->Execute();
        if (b) {
            this->mark = true;
            CancelTransitions(i);  //erase OTHER events (in the channel) already registered by the transitions of this state, as the state tried its every possible transitions
#ifdef STATE_DEBUG
            printf("transition %s true\n", transitions[i]->name.c_str());
#endif
            return true;
        }
        else {
#ifdef STATE_DEBUG
            printf("transition %s false\n", transitions[i]->name.c_str());
#endif
        }
    }
    this->mark = false;
    return false;
}

void State::CancelTransitions(int i) {
    for (int j = 0; j < transitions.size(); j++) {
        if (j != i) {
#ifdef STATE_DEBUG
            printf("CANCEL transition: %s\n",transitions[j]->name.c_str());
#endif
            transitions[j]->Cancel();
        }
    }
}

//return false either no sub states or no transitions are enabled in the sub states
bool State::TryExecuteSubstates(std::vector<std::shared_ptr<State>> s) {
    for (int i = 0; i < s.size(); i++) {
        // printf("state index : %d; stage: %d\n", i, states[i]->stage);
        // there should be only one active state in a single state machine
        if (s[i]->stage == s_Inactive) continue;
        else {
            s[i]->Execute();
            return s[i]->mark;      //keep trying the transitions at the same level if there is transition from one state to another
        }
    }
    return false;
}

}

Transition

Similarly to states, transitions must extend the class Transition. They can provide a number of functions:Reg,Check,Cancel,ConditionandAction. The first three are necessary if the transition has an event as a trigger, the fourth if the transition has a condition and the final if the transition has an action. A subclass ofTransitionmay implement five optional functions:

  • void Reg(): this function is only implemented if the transition has a trigger with an event. In this case, the functionRegof a channel must be called (with appropriate parameters) and the event returned by the call must be stored in a variable such asevent;
  • bool Check(): this function is implemented to check the occurrence of the registered event. It calls theCheckfunction of the channel on the event produced byReg;
  • void Cancel(): this function calls the method Cancel of the channel with the event produced byRegas a parameter;
  • bool Condition(): this function implements the condition of the transition;
  • void Action(): this function implements the action of the transition. If the transition's trigger contains an event, this function must call the functionAcceptorAcceptAndDeleteof the channel (on the event obtained fromReg). The functionAcceptshould be called if the channel is synchronous, andAcceptAndDeleteshould be called if the channel is asynchronous.

In the obstacle avoidance example, the transition t1 has a trigger of the formobstacle?dirand also reset the clock T. This transition is implemented as the classt1, whereRegis implemented as a function that calls the functionRegof the channelobstacleof the state machine with source name StmMovement (identifying the state machine StmMovement) and undefined parameterOptional<Loc>(). The result of theRegfunction of the channel is a pointer to an event that is recorded in the variableevent.

TheCheckfunction simply returns the result of calling theCheckfunction of the channelobstacle. If the Check function return true, the first parameter of the event to the state machine variabledirusing the functionget<0>applied to the event. The event must be accepted to mark it as treated and set to thenullptr. In the case of the transition t1, the action finishes with a call toAcceptAndDeletebecause the channelobstacleis asynchronous. If it were synchronous, the methodAcceptshould be called, which will only delete the event if both sides of the communication have accepted it. If the check function return false, the functionCancelcalls the functionCancelof the same channel. All these calls are guarded by a check on the variableeventthat guarantees it is not null.

Transition t2 illustrates the implementation of a transition with a condition.

The condition calculates the time steps elapsed recorded in T . The time step is increased in every cycle of the simulation followed by the execution of the state machine. If the time passes certain threshold, transition t2 is trigger.

results matching ""

    No results matching ""