Skip to content

Commit 2a123a6

Browse files
committed
Initial Commit
1 parent 47df069 commit 2a123a6

File tree

4 files changed

+294
-0
lines changed

4 files changed

+294
-0
lines changed

README.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,85 @@
11
# StateMachine
22
Create State Machines with class
3+
4+
## Advantages
5+
Create a State Machine has never been easier!
6+
There are just 2 components `StateMachine` and `State`.
7+
There are some state's decorators that will help develop your applications logic.
8+
9+
## StateMachine
10+
The StateMachine is the orchestrator of the states.
11+
It has a generic `Context` parameter that allows to provide some informations or dependencies to the states.
12+
13+
## State
14+
Subclass the `State` class and override the `handleState` method.
15+
16+
## State Decorators
17+
18+
### Delay
19+
The `State`'s `delay` method will return a new `DelayedState` that will switch to the new state after a period of time
20+
21+
### Throttle
22+
The `State`'s `throttle` method will return a new `ThrottledState` call the `handleState` max once every period of time
23+
24+
### Recovery
25+
The `State`'s `recoveryAfter` method will switch to a recoveryState if the state is still running after a period of time
26+
27+
## Example
28+
```c
29+
// This example creates a StateMachine with 3 states
30+
31+
// Include the StateMachine library found here :
32+
// https://github.com/Gruppio/StateMachine
33+
34+
#include <Arduino.h>
35+
#include <StateMachine.h>
36+
37+
struct StateContext {
38+
bool isCompleted = false;
39+
};
40+
41+
class FinalState: public State<StateContext*> {
42+
void handleState(StateMachine<StateContext*> *stateMachine) {
43+
Serial.println("Perform FinalState work");
44+
stateMachine->context->isCompleted = true;
45+
}
46+
};
47+
48+
class SecondState: public State<StateContext*> {
49+
void handleState(StateMachine<StateContext*> *stateMachine) {
50+
Serial.println("Perform SecondState work");
51+
// Will call the `handleState` of `FinalState` max once every `100ms`
52+
stateMachine->setState(
53+
(new FinalState())->throttle(100);
54+
);
55+
}
56+
};
57+
58+
class InitialState: public State<StateContext*> {
59+
void handleState(StateMachine<StateContext*> *stateMachine) {
60+
Serial.println("Perform BeginState work");
61+
// Will transition to `SecondState` after 1000ms
62+
stateMachine->setState(
63+
(new SecondState())->delay(1000);
64+
);
65+
}
66+
};
67+
68+
StateContext *stateContext;
69+
StateMachine<StateContext*> *stateMachine;
70+
71+
void setup() {
72+
stateContext = new StateContext();
73+
stateMachine = new StateMachine<StateContext*>(stateContext, new InitialState());
74+
}
75+
76+
void loop() {
77+
stateMachine->handleState();
78+
}
79+
```
80+
81+
## If you like this project please:
82+
83+
<a href="https://www.buymeacoffee.com/gruppio" target="_blank"><img src="https://raw.githubusercontent.com/Gruppio/Sonoff-Homekit/images/images/buymeacoffee.png" alt="Buy Me A Coffee" width="300" ></a>
84+
85+
## Thank you!

library.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "StateMachine",
3+
"keywords": "state, state machine, design, pattern, gof, gang, of, four, machine, automation",
4+
"description": "Create State Machines with class",
5+
"repository": {
6+
"type": "git",
7+
"url": "https://github.com/Gruppio/StateMachine.git"
8+
},
9+
"version": "1.0.0",
10+
"exclude": [
11+
"*.png",
12+
"*.zip"
13+
],
14+
"frameworks": "arduino",
15+
"platforms": "*"
16+
}

library.properties

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
name=StateMachine
2+
version=1.0.0
3+
author=Michele Gruppioni @Gruppio
4+
maintainer=Michele Gruppioni @Gruppio
5+
sentence=Create State Machines with class
6+
paragraph=The State Machine design pattern
7+
category=Signal Input/Output
8+
url=https://github.com/Gruppio/StateMachine.git
9+
architectures=*

src/StateMachine.h

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
//
2+
// StateMachine.h
3+
// StateMachine
4+
//
5+
// Created by Michele Gruppioni on 21/03/21.
6+
//
7+
8+
#ifndef StateMachine_h
9+
#define StateMachine_h
10+
11+
extern unsigned long millis();
12+
13+
template <class Context>
14+
class State;
15+
16+
template <class Context>
17+
class ThrottledState;
18+
19+
template <class Context>
20+
class DelayedState;
21+
22+
template <class Context>
23+
class RecoveryState;
24+
25+
// StateMachine
26+
27+
template <class Context>
28+
class StateMachine
29+
{
30+
public:
31+
Context context;
32+
33+
private:
34+
State<Context> *state;
35+
36+
public:
37+
StateMachine(Context context, State<Context> *initialState);
38+
void setState(State<Context> *newState);
39+
void handleState();
40+
};
41+
42+
template <class Context>
43+
StateMachine<Context>::StateMachine(Context context, State<Context> *initialState) : context(context),
44+
state(initialState)
45+
{
46+
}
47+
48+
template <class Context>
49+
void StateMachine<Context>::setState(State<Context> *newState)
50+
{
51+
state = newState;
52+
}
53+
54+
template <class Context>
55+
void StateMachine<Context>::handleState()
56+
{
57+
state->handleState(this);
58+
}
59+
60+
// State
61+
62+
template <class Context>
63+
class State
64+
{
65+
public:
66+
virtual void handleState(StateMachine<Context> *stateContext) = 0;
67+
68+
State<Context>* throttle(unsigned long interval);
69+
State<Context>* delay(unsigned long interval);
70+
State<Context>* recoveryAfter(unsigned long maxDuration, State<Context> *recoveryState);
71+
};
72+
73+
template <class Context>
74+
State<Context>* State<Context>::throttle(unsigned long interval)
75+
{
76+
return new ThrottledState<Context>(interval, this);
77+
}
78+
79+
template <class Context>
80+
State<Context>* State<Context>::delay(unsigned long interval)
81+
{
82+
return new DelayedState<Context>(interval, this);
83+
}
84+
85+
template <class Context>
86+
State<Context>* State<Context>::recoveryAfter(unsigned long maxDuration, State<Context> *recoveryState)
87+
{
88+
return new RecoveryState<Context>(maxDuration, recoveryState, this);
89+
}
90+
91+
// ThrottledState
92+
93+
template <typename Context>
94+
class ThrottledState : public State<Context>
95+
{
96+
private:
97+
unsigned long interval;
98+
State<Context> *nextState;
99+
unsigned long lastTimeRunned = 0;
100+
101+
public:
102+
ThrottledState(unsigned long interval, State<Context> *nextState);
103+
void handleState(StateMachine<Context> *stateMachine);
104+
};
105+
106+
template <class Context>
107+
ThrottledState<Context>::ThrottledState(unsigned long interval, State<Context> *nextState) :
108+
interval(interval),
109+
nextState(nextState)
110+
{
111+
}
112+
113+
template <class Context>
114+
void ThrottledState<Context>::handleState(StateMachine<Context> *stateMachine)
115+
{
116+
if (millis() >= lastTimeRunned + interval)
117+
{
118+
lastTimeRunned = millis();
119+
nextState->handleState(stateMachine);
120+
}
121+
}
122+
123+
// DelayedState
124+
125+
template <class Context>
126+
class DelayedState : public State<Context>
127+
{
128+
private:
129+
unsigned long delayValue;
130+
State<Context> *nextState;
131+
unsigned long startTime = millis();
132+
133+
public:
134+
DelayedState(unsigned long delayValue, State<Context> *nextState);
135+
void handleState(StateMachine<Context> *stateMachine);
136+
};
137+
138+
template <class Context>
139+
DelayedState<Context>::DelayedState(unsigned long delayValue, State<Context> *nextState):
140+
delayValue(delayValue),
141+
nextState(nextState) {
142+
}
143+
144+
template <class Context>
145+
void DelayedState<Context>::handleState(StateMachine<Context> *stateMachine) {
146+
if(millis() > startTime + delayValue) {
147+
stateMachine->setState(nextState);
148+
}
149+
}
150+
151+
// RecoveryState
152+
153+
template <class Context>
154+
class RecoveryState : public State<Context>
155+
{
156+
private:
157+
unsigned long maxDuration;
158+
State<Context> *recoveryState;
159+
State<Context> *state;
160+
unsigned long startTime = 0;
161+
162+
public:
163+
RecoveryState(unsigned long maxDuration, State<Context> *recoveryState, State<Context> *state);
164+
void handleState(StateMachine<Context> *stateMachine);
165+
};
166+
167+
template <class Context>
168+
RecoveryState<Context>::RecoveryState(unsigned long maxDuration, State<Context> *recoveryState, State<Context> *state):
169+
maxDuration(maxDuration),
170+
recoveryState(recoveryState),
171+
state(state) {
172+
}
173+
174+
template <class Context>
175+
void RecoveryState<Context>::handleState(StateMachine<Context> *stateMachine) {
176+
if (startTime == 0)
177+
startTime = millis();
178+
179+
if (millis() > startTime + maxDuration) {
180+
stateMachine->setState(recoveryState);
181+
} else {
182+
stateMachine->handleState(stateMachine);
183+
}
184+
}
185+
186+
#endif /* StateMachine_h */

0 commit comments

Comments
 (0)