|
|
WIP
|
|
|
# WIP
|
|
|
|
|
|
Hierarchical State Machine |
|
|
\ No newline at end of file |
|
|
Hierarchical State Machine
|
|
|
|
|
|
## Example
|
|
|
This example implements the following very simple hierarchical state machine behavior:
|
|
|
|
|
|

|
|
|
|
|
|
Whenever the state machine moves to state `S2` (super-state), it will automatically transition to its first sub-state, that is `S3`.
|
|
|
We can notice that state `S3` only has a self-loop trasition that is triggered by event `EV_2`. However, if the HSM receives an event `EV_1` while being in state `S3`, the event is handled by state `S2`, which is a super-state of `S3`.
|
|
|
|
|
|
No action is specified for the involved events (`EV_ENTRY`, `EV_EXIT`, `EV_1`, `EV_2`). They simply trigger the transitions.
|
|
|
|
|
|
#### MyHSM.h
|
|
|
As always when dealing with events and topics, after we include the required dependencies, we have to define the set of possible events and topics.
|
|
|
```cpp
|
|
|
#pragma once
|
|
|
|
|
|
#include "events/HSM.h"
|
|
|
#include "events/EventBroker.h"
|
|
|
|
|
|
enum ExampleEvents : uint8_t
|
|
|
{
|
|
|
EV_1 = EV_FIRST_SIGNAL,
|
|
|
EV_2
|
|
|
};
|
|
|
|
|
|
enum ExampleTopics : uint8_t
|
|
|
{
|
|
|
TOPIC_1
|
|
|
};
|
|
|
```
|
|
|
|
|
|
Then we can define the MyHSM class and its states, which are identified by the two methods `state_S1`, `state_S2` (which is a super-state) and `state_S3` (which is a sub-state of `state_S2`).
|
|
|
|
|
|
```cpp
|
|
|
class MyHSM : public HSM<MyHSM>
|
|
|
{
|
|
|
public:
|
|
|
MyHSM()
|
|
|
: HSM(&MyHSM::state_initialization), // set HSM initial state to state_S1
|
|
|
last_event(0)
|
|
|
{
|
|
|
// make this object to subscribe to TOPIC_1
|
|
|
sEventBroker->subscribe(this, TOPIC_1);
|
|
|
}
|
|
|
|
|
|
~MyHSM()
|
|
|
{
|
|
|
// unsubscribe from all the topics this object was subscribed to
|
|
|
sEventBroker->unsubscribe(this);
|
|
|
}
|
|
|
|
|
|
State state_initialization(const Event& ev)
|
|
|
{ // this is just a ficticious state
|
|
|
// that automatically triggers a transition
|
|
|
// to the HSM initial state
|
|
|
UNUSED(ev);
|
|
|
return transition(&MyHSM::state_S1);
|
|
|
}
|
|
|
|
|
|
State state_S1(const Event& ev)
|
|
|
{
|
|
|
State ret_state = HANDLED;
|
|
|
|
|
|
switch (ev.sig)
|
|
|
{
|
|
|
case EV_INIT:
|
|
|
case EV_ENTRY:
|
|
|
case EV_EXIT:
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
case EV_1:
|
|
|
{
|
|
|
TRACE("S1: EV_1 \n");
|
|
|
// self-loop on state_S1
|
|
|
ret_state = transition(&MyHSM::state_S1);
|
|
|
break;
|
|
|
}
|
|
|
case EV_2:
|
|
|
{
|
|
|
TRACE("S1: EV_2 ---> S2 \n");
|
|
|
ret_state = transition(&MyHSM::state_S2);
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
|
{
|
|
|
ret_state = tran_super(&MyHSM::Hsm_top); // no parent state
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
last_event = ev.sig;
|
|
|
|
|
|
return ret_state;
|
|
|
}
|
|
|
|
|
|
State state_S2(const Event& ev)
|
|
|
{
|
|
|
State ret_state = HANDLED;
|
|
|
|
|
|
switch (ev.sig)
|
|
|
{
|
|
|
case EV_INIT:
|
|
|
{
|
|
|
// this is a super-state
|
|
|
// move to the first sub-state
|
|
|
TRACE("S2: start from S3 \n");
|
|
|
ret_state = transition(&MyHSM::state_S3);
|
|
|
}
|
|
|
case EV_ENTRY:
|
|
|
case EV_EXIT:
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
case EV_1:
|
|
|
{
|
|
|
TRACE("S2: EV_1 ---> S1 \n");
|
|
|
ret_state = transition(&MyHSM::state_S1);
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
|
{
|
|
|
ret_state = tran_super(&MyHSM::Hsm_top); // no parent state
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
last_event = ev.sig;
|
|
|
|
|
|
return ret_state;
|
|
|
}
|
|
|
|
|
|
State state_S3(const Event& ev)
|
|
|
{
|
|
|
State ret_state = HANDLED;
|
|
|
|
|
|
switch (ev.sig)
|
|
|
{
|
|
|
case EV_INIT:
|
|
|
case EV_ENTRY:
|
|
|
case EV_EXIT:
|
|
|
{
|
|
|
break;
|
|
|
}
|
|
|
case EV_2:
|
|
|
{
|
|
|
TRACE("S3: EV_2 \n");
|
|
|
// self-loop on state_S3
|
|
|
ret_state = transition(&MyHSM::state_S3);
|
|
|
break;
|
|
|
}
|
|
|
default:
|
|
|
{
|
|
|
ret_state = tran_super(&MyHSM::state_S2); // try with parent state S2
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
last_event = ev.sig;
|
|
|
|
|
|
return ret_state;
|
|
|
}
|
|
|
|
|
|
private:
|
|
|
uint8_t last_event;
|
|
|
};
|
|
|
```
|
|
|
|
|
|
#### test-hsm.cpp
|
|
|
|
|
|
```cpp
|
|
|
#include <Common.h>
|
|
|
#include "MyHSM.h"
|
|
|
|
|
|
using namespace miosix;
|
|
|
|
|
|
int main()
|
|
|
{
|
|
|
EventBroker* broker = EventBroker::getInstance(); // singleton object
|
|
|
broker->start();
|
|
|
|
|
|
MyHSM hsm;
|
|
|
|
|
|
if (hsm.start())
|
|
|
{
|
|
|
// trigger self-loop on state S1
|
|
|
broker->post(Event{EV_1}, TOPIC_1);
|
|
|
Thread::sleep(100);
|
|
|
|
|
|
// trigger transition to state S2
|
|
|
// which in turn will automatically
|
|
|
// start from state S3 (the first sub-state)
|
|
|
broker->post(Event{EV_2}, TOPIC_1);
|
|
|
Thread::sleep(100);
|
|
|
|
|
|
// trigger self-loop on state S3
|
|
|
broker->post(Event{EV_2}, TOPIC_1);
|
|
|
Thread::sleep(100);
|
|
|
|
|
|
// the parent state S2 handles the event
|
|
|
// and triggers transition to state S1
|
|
|
broker->post(Event{EV_1}, TOPIC_1);
|
|
|
Thread::sleep(100);
|
|
|
|
|
|
hsm.stop();
|
|
|
broker->stop();
|
|
|
}
|
|
|
else {
|
|
|
TRACE("Failed to start MyHSM\n");
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
```
|
|
|
#### Output
|
|
|
|
|
|
```sh
|
|
|
0.12> S1: EV_1
|
|
|
0.22> S1: EV_2 ---> S2
|
|
|
0.23> S2: start from S3
|
|
|
0.32> S3: EV_2
|
|
|
0.42> S2: EV_1 ---> S1
|
|
|
``` |
|
|
\ No newline at end of file |