digint / digint/tinyfsm

Adding Context to tinyfsm

Open
#45 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
C++
Stars
1.2k
Forks
193
PR merge metrics
No merged PRs in 30d

Description

Hi,
I am experimenting with tinysfsm. Coding a simple traffic light with on/off and three colors is quite easy.

![Image](https://github.com/user-attachments/assets/745d5859-8d77-42c4-91e3-3af1095dbb34)

But I wonder, how I can avoid using a global variable to use as a context for sharing state with the outside for more complex FSM.
It this time, I have added a template parameter with an inline static to store this context, but I am not satisfied, even if ti's working well.
Any idea to do better than that ?

Regards,

PS: Code added to have the idea

```
#include "tinyfsm_ctx.hpp"

#include
#include

#include
#include
#include

using Duration = std::chrono::steady_clock::duration;

using namespace std::chrono_literals;

namespace {
inline constexpr Duration DURATION_ORANGE = 2s;
inline constexpr Duration DURATION_RED = 5s;
inline constexpr Duration DURATION_GREEN = 8s;
} // namespace

struct TriColorLight
{
Duration remaining = 0s;
bool green = false;
bool orange = false;
bool red = false;

void off() { green = orange = red = false; }

void display(const bool is_red) const
{
using namespace std::chrono;

std::cout << "\r\33[2K" << std::flush;
if (!green && !orange && !red) {
std::cout << "OFF";
}
else {
std::cout << fmt::format(fg(green ? fmt::color::green : fmt::color::gray), "{:>10}", "GREEN");
std::cout << fmt::format(fg(orange ? fmt::color::orange : fmt::color::gray), "{:>10}", "ORANGE");
std::cout << fmt::format(fg(red ? fmt::color::red : fmt::color::gray), "{:>10}", "RED");
std::cout << fmt::format("{:>18.3f}", duration_cast>(remaining).count());
std::cout << fmt::format(fg(fmt::color::red), "{:>10}", is_red ? "***" : "");
}
std::cout << std::flush;
}
};

// ------------------------
// Events
struct switch_on_off : tinyfsm::Event
{
};
struct tick : tinyfsm::Event
{
Duration elapsed_time;
};

// ------------------------
// States
class Stopped;
class Started;
// Substates of Started
class Started_Orange;
class Started_Red;
class Started_Green;

// ------------------------
// FSM
class TricolorLightFSM : public tinyfsm::Fsm
{
public:
virtual void react(const tick&) {}
virtual void react(const switch_on_off&) {}

virtual void entry() {}
virtual void exit() {}

static void reset();
};

// ------------------------
// States
class Stopped final : public TricolorLightFSM
{
void entry() override { context().off(); }

void react(const switch_on_off&) override { transit(); }
};

class Started : public TricolorLightFSM
{
void entry() override { dispatch(tick{}); }

void react(const switch_on_off&) override { transit(); }
void react(const tick&) override { transit(); }
};

class Started_Orange final : public Started
{
void entry() override
{
context().remaining = DURATION_ORANGE;
context().orange = true;
}
void exit() override { context().orange = false; }

void react(const tick& t) override
{
context().remaining -= t.elapsed_time;
if (context().remaining <= 0s) {
transit();
}
}
};

class Started_Red final : public Started
{
void entry() override
{
context().remaining = DURATION_RED;
context().red = true;
}
void exit() override { context().red = false; }

void react(const tick& t) override
{
context().remaining -= t.elapsed_time;
if (context().remaining <= 0s) {
transit();
}
}
};

class Started_Green final : public Started
{
void entry() override
{
context().remaining = DURATION_GREEN;
context().green = true;
}

void exit() override { context().green = false; }

void react(const tick& t) override
{
context().remaining -= t.elapsed_time;
if (context().remaining <= 0s) {
transit();
}
}
};

FSM_INITIAL_STATE_CTX(TricolorLightFSM, TriColorLight, Stopped)

// ------------------------------
int main()
{
TriColorLight tricolor_light;

TricolorLightFSM::set_context(tricolor_light);
TricolorLightFSM::start();

auto start_time = std::chrono::steady_clock::now();

TricolorLightFSM::dispatch(switch_on_off{});

for (int i = 0; i < 1000000; ++i) {
auto current_time = std::chrono::steady_clock::now();

TricolorLightFSM::dispatch(tick{.elapsed_time = current_time - start_time});

tricolor_light.display(TricolorLightFSM::is_in_state());

start_time = current_time;
std::this_thread::sleep_for(25ms);
}
}
````

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.