Skip to content

Commit

Permalink
Allow FSM states to declare them self as resumable and persist this i…
Browse files Browse the repository at this point in the history
…nfo to NVS for resuming the FSM after boot
  • Loading branch information
ngandrass committed Sep 2, 2024
1 parent 6e4e753 commit 7840a30
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 16 deletions.
12 changes: 12 additions & 0 deletions include/FSM.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ class FSM {
*/
~FSM();

/**
* @brief Resumes the FSM to the last state according to NVS data
*/
void resume();

/**
* @brief Performs a transition to the given next state
*
* @param next The state to transition to.
*/
void transition(std::unique_ptr<FSMState> next);

/**
* @brief Retrieves the tick rate of this FSM
*
Expand Down
1 change: 1 addition & 0 deletions include/FSMGlobals.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
* FSM::persistGlobals() and FSM::restoreGlobals() respectively.
*/
typedef struct {
const char* lastRememberedState = "DisplayPrideFlag"; //!< Name of the state that should be resumed upon reboot
uint8_t menuMainPointerIdx = 0; //!< MenuMain: Index of the menu cursor
uint8_t prideFlagModeIdx = 0; //!< DisplayPrideFlag: Mode selector
uint8_t animationModeIdx = 0; //!< DisplayAnimation: Mode selector
Expand Down
10 changes: 10 additions & 0 deletions include/FSMState.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ class FSMState {
*/
virtual const char* getName();

/**
* @brief If true, the FSM will persist this state and resume to it after
* reboot, if no other transition to another rememberable state happened since
*
* @return True, if the FSM should remember this state and resume to it upon reboot
*/
virtual bool shouldBeRemembered();

/**
* @brief Provides access to the tick rate of this state
*
Expand Down Expand Up @@ -135,6 +143,7 @@ struct DisplayPrideFlag : public FSMState {
unsigned int switchdelay_ms = 5000;

virtual const char* getName() override;
virtual bool shouldBeRemembered() override;
virtual const unsigned int getTickRateMs() override;

virtual void entry() override;
Expand All @@ -150,6 +159,7 @@ struct DisplayAnimation : public FSMState {
uint32_t tick = 0;

virtual const char* getName() override;
virtual bool shouldBeRemembered() override;
virtual const unsigned int getTickRateMs() override;

virtual void entry() override;
Expand Down
62 changes: 47 additions & 15 deletions src/FSM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,54 @@ FSM::FSM(unsigned int tickrate_ms)
this->globals = std::make_shared<FSMGlobals>();
this->state = std::make_unique<DisplayPrideFlag>();
this->state->attachGlobals(this->globals);
this->state->entry();
}

FSM::~FSM() {
this->state->exit();
}

void FSM::resume() {
this->restoreGlobals();

if (strcmp(this->globals->lastRememberedState, "DisplayPrideFlag") == 0) {
this->transition(std::make_unique<DisplayPrideFlag>());
return;
}

if (strcmp(this->globals->lastRememberedState, "DisplayAnimation") == 0) {
this->transition(std::make_unique<DisplayAnimation>());
return;
}

LOGF_WARNING("(FSM) Failed to resume to unknown state: %s\r\n", this->globals->lastRememberedState);
this->transition(std::make_unique<DisplayPrideFlag>());
}

void FSM::transition(std::unique_ptr<FSMState> next) {
if (next == nullptr) {
LOG_WARNING("(FSM) Failed to transition to null state. Aborting.");
return;
}

// State exit
LOGF_INFO("(FSM) Transition %s -> %s\r\n", this->state->getName(), next->getName());
this->state->exit();

// Persist globals if state dirtied it or next state wants to be persisted
if (next->shouldBeRemembered()) {
this->globals->lastRememberedState = next->getName();
}
if (this->state->isGlobalsDirty() || next->shouldBeRemembered()) {
this->persistGlobals();
}

// Transition to next state
this->state = std::move(next);
this->state->attachGlobals(this->globals);
this->state_last_run = 0;
this->state->entry();
}

unsigned int FSM::getTickRateMs() {
return this->tickrate_ms;
}
Expand Down Expand Up @@ -148,20 +189,7 @@ void FSM::handle(unsigned int num_events) {

// Handle state transition
if (next != nullptr) {
// State exit
LOGF_INFO("(FSM) Transition %s -> %s\r\n", this->state->getName(), next->getName());
this->state->exit();

// Persist globals if state dirtied it
if (this->state->isGlobalsDirty()) {
this->persistGlobals();
}

// Transition to next state
this->state = std::move(next);
this->state->attachGlobals(this->globals);
this->state_last_run = 0;
this->state->entry();
this->transition(move(next));
}
}
}
Expand All @@ -172,6 +200,8 @@ void FSM::persistGlobals() {
LOGF_INFO("(FSM) Persisting FSM state data to NVS area: %s\r\n", this->NVS_NAMESPACE);
pref.clear();
LOG_DEBUG("(FSM) -> Clear storage area");
pref.putString("resumeState", this->globals->lastRememberedState);
LOGF_DEBUG("(FSM) -> resumeState = %s\r\n", this->globals->lastRememberedState);
pref.putUInt("prideFlagMode", this->globals->prideFlagModeIdx);
LOGF_DEBUG("(FSM) -> prideFlagMode = %d\r\n", this->globals->prideFlagModeIdx);
pref.putUInt("animationMode", this->globals->animationModeIdx);
Expand All @@ -183,6 +213,8 @@ void FSM::restoreGlobals() {
Preferences pref;
pref.begin(this->NVS_NAMESPACE, true);
LOGF_INFO("(FSM) Restored FSM state data from NVS area: %s\r\n", this->NVS_NAMESPACE);
this->globals->lastRememberedState = pref.getString("resumeState", "DisplayPrideFlag").c_str();
LOGF_DEBUG("(FSM) -> resumeState = %s\r\n", this->globals->lastRememberedState);
this->globals->prideFlagModeIdx = pref.getUInt("prideFlagMode", 0);
LOGF_DEBUG("(FSM) -> prideFlagMode = %d\r\n", this->globals->prideFlagModeIdx);
this->globals->animationModeIdx = pref.getUInt("animationMode", 0);
Expand Down
4 changes: 3 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,6 @@ void setup() {
// Init board
EFBoard.setup();
EFLed.init(30);
fsm.restoreGlobals();

// Touchy stuff
EFTouch.init();
Expand All @@ -163,6 +162,9 @@ void setup() {
EFTouch.attachInterruptOnRelease(EFTouchZone::Nose, isr_noseRelease);
EFTouch.attachInterruptOnShortpress(EFTouchZone::Nose, isr_noseShortpress);
EFTouch.attachInterruptOnLongpress(EFTouchZone::Nose, isr_noseLongpress);

// Get FSM going
fsm.resume();
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/states/DisplayAnimation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ const char* DisplayAnimation::getName() {
return "DisplayAnimation";
}

bool DisplayAnimation::shouldBeRemembered() {
return true;
}

const unsigned int DisplayAnimation::getTickRateMs() {
return animations[this->globals->animationModeIdx % DISPLAY_ANIMATION_NUM_TOTAL].tickrate;
}
Expand Down
4 changes: 4 additions & 0 deletions src/states/DisplayPrideFlag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ const char* DisplayPrideFlag::getName() {
return "DisplayPrideFlag";
}

bool DisplayPrideFlag::shouldBeRemembered() {
return true;
}

const unsigned int DisplayPrideFlag::getTickRateMs() {
return 20;
}
Expand Down
4 changes: 4 additions & 0 deletions src/states/FSMState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ bool FSMState::isGlobalsDirty() {
return this->is_globals_dirty;
}

bool FSMState::shouldBeRemembered() {
return false;
}

const char* FSMState::getName() {
return "FSMState";
}
Expand Down

0 comments on commit 7840a30

Please sign in to comment.