-
Notifications
You must be signed in to change notification settings - Fork 185
Open
Description
Hi,
I am experimenting with tinysfsm. Coding a simple traffic light with on/off and three colors is quite easy.
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 <fmt/color.h>
#include <fmt/format.h>
#include <chrono>
#include <iostream>
#include <thread>
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<duration<double>>(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<TricolorLightFSM, TriColorLight>
{
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<Started>(); }
};
class Started : public TricolorLightFSM
{
void entry() override { dispatch(tick{}); }
void react(const switch_on_off&) override { transit<Stopped>(); }
void react(const tick&) override { transit<Started_Orange>(); }
};
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<Started_Red>();
}
}
};
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<Started_Green>();
}
}
};
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<Started_Orange>();
}
}
};
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<Started_Red>());
start_time = current_time;
std::this_thread::sleep_for(25ms);
}
}
Metadata
Metadata
Assignees
Labels
No labels