From 3044f752b590f99b9d58e80375923d0a96603c36 Mon Sep 17 00:00:00 2001 From: Matti Airas Date: Sun, 1 Sep 2024 18:32:41 +0300 Subject: [PATCH] Rename Reactions to Events --- README.md | 48 +++--- examples/minimal/src/main.cpp | 14 +- examples/torture_test/src/main.cpp | 56 +++---- src/ReactESP.cpp | 122 ++++++--------- src/ReactESP.h | 242 ++++++++++++++--------------- 5 files changed, 227 insertions(+), 255 deletions(-) diff --git a/README.md b/README.md index b031bee..8772a8e 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ An asynchronous programming and event loop library for the ESP32 and other micro The library is at the core of the [SensESP](https://github.com/SignalK/SensESP) project but is completely generic and can be used for standalone projects without issues. -This library gets much of its inspiration (and some code) from [`Reactduino`](https://github.com/Reactduino/Reactduino). `ReactESP`, however, has been internally re-implemented for maintainability and readability, and has significantly better performance when there are lots of defined reactions. It also supports arbitrary callables as callbacks, allowing parametric creation of callback functions. +This library gets much of its inspiration (and some code) from [`Reactduino`](https://github.com/Reactduino/Reactduino). `ReactESP`, however, has been internally re-implemented for maintainability and readability, and has significantly better performance when there are lots of defined events. It also supports arbitrary callables as callbacks, allowing parametric creation of callback functions. ## Blink @@ -37,25 +37,25 @@ Using ReactESP, the sketch can be rewritten to the following: using namespace reactesp; -ReactESP app; +EventLoop event_loop; setup() { pinMode(LED_BUILTIN, OUTPUT); - app.onRepeat(1000, [] () { + event_loop.onRepeat(1000, [] () { static bool state = false; digitalWrite(LED_BUILTIN, state = !state); }); } void loop() { - app.tick(); + event_loop.tick(); } ``` -Instead of directly defining the program logic in the `loop()` function, _reactions_ are defined in the `setup()` function. An event is a function that is executed when a certain event happens. In this case, the event is that the function should repeat every second, as defined by the `onRepeat()` method call. The second argument to the `onRepeat()` method is a [lambda function](http://en.cppreference.com/w/cpp/language/lambda) that is executed every time the reaction is triggered. If the syntax feels weird, you can also use regular named functions instead of lambdas. +Instead of directly defining the program logic in the `loop()` function, _events_ are defined in the `setup()` function. An event is a function that is executed when a certain event happens. In this case, the event is that the function should repeat every second, as defined by the `onRepeat()` method call. The second argument to the `onRepeat()` method is a [lambda function](http://en.cppreference.com/w/cpp/language/lambda) that is executed every time the event is triggered. If the syntax feels weird, you can also use regular named functions instead of lambdas. -The `app.tick()` call in the `loop()` function is the main loop of the program. It is responsible for calling the reactions that have been defined. You can also add additional code to the `loop()` function, any delays or other long-executing code should be avoided. +The `event_loop.tick()` call in the `loop()` function is the main loop of the program. It is responsible for calling the events that have been defined. You can also add additional code to the `loop()` function, any delays or other long-executing code should be avoided. ## Why Bother? @@ -146,28 +146,28 @@ This solves Charlie's problem, but it's quite verbose. Using ReactESP, Charlie c using namespace reactesp; -EventLoop app; +EventLoop event_loop; void setup() { Serial.begin(9600); pinMode(LED_BUILTIN, OUTPUT); - app.onAvailable(&Serial, [] () { + event_loop.onAvailable(&Serial, [] () { Serial.write(Serial.read()); digitalWrite(LED_BUILTIN, HIGH); - app.onDelay(1000, [] () { digitalWrite(LED_BUILTIN, LOW); }); + event_loop.onDelay(1000, [] () { digitalWrite(LED_BUILTIN, LOW); }); }); } void loop() { - app.tick(); + event_loop.tick(); } ``` ## Advanced callback support -Callbacks can be not just void pointers but any callable supported by `std::function`. This means they can use lambda captures or argument binding using `std::bind`. For example, the following code creates 20 different repeating reactions updating different fields of an array: +Callbacks can be not just void pointers but any callable supported by `std::function`. This means they can use lambda captures or argument binding using `std::bind`. For example, the following code creates 20 different repeating events updating different fields of an array: ```c++ static int timer_ticks[20]; @@ -175,7 +175,7 @@ static int timer_ticks[20]; for (int i=0; i<20; i++) { timer_ticks[i] = 0; int delay = (i+1)*(i+1); - app.onRepeat(delay, [i]() { + event_loop.onRepeat(delay, [i]() { timer_ticks[i]++; }); } @@ -196,39 +196,39 @@ This can be done either globally by placing the following statement in the sourc This shouldn't be done in header files, however! Alternatively, the `reactesp::` prefix can be used when using the library: - reactesp::EventLoop app; + reactesp::EventLoop event_loop; ### Event Registration Functions All of the registration functions return an `Event` object pointer. This can be used to store and manipulate -the reaction. `react_callback` is a typedef for `std::function` and can therefore be any callable supported by the C++ standard template library. +the event. `react_callback` is a typedef for `std::function` and can therefore be any callable supported by the C++ standard template library. ```cpp -DelayEvent app.onDelay(uint32_t t, react_callback cb); +DelayEvent event_loop.onDelay(uint32_t t, react_callback cb); ``` Delay the executation of a callback by `t` milliseconds. ```cpp -RepeatEvent app.onRepeat(uint32_t t, react_callback cb); +RepeatEvent event_loop.onRepeat(uint32_t t, react_callback cb); ``` Repeatedly execute a callback every `t` milliseconds. ```cpp -StreamEvent app.onAvailable(Stream *stream, react_callback cb); +StreamEvent event_loop.onAvailable(Stream *stream, react_callback cb); ``` Execute a callback when there is data available to read on an input stream (such as `&Serial`). ```cpp -ISREvent app.onInterrupt(uint8_t pin_number, int mode, react_callback cb); +ISREvent event_loop.onInterrupt(uint8_t pin_number, int mode, react_callback cb); ``` Execute a callback when an interrupt number fires. This uses the same API as the `attachInterrupt()` Arduino function. ```cpp -TickEvent app.onTick(react_callback cb); +TickEvent event_loop.onTick(react_callback cb); ``` Execute a callback on every tick of the event loop. @@ -247,13 +247,19 @@ Remove the event from the execution queue. - [`Minimal`](examples/minimal/src/main.cpp): A minimal example with two timers switching the LED state. -- [`Torture test`](examples/torture_test/src/main.cpp): A stress test of twenty simultaneous repeat reactions as well as a couple of interrupts, a stream, and a tick reaction. For kicks, try changing `NUM_TIMERS` to 200. Program performance will be practically unchanged! +- [`Torture test`](examples/torture_test/src/main.cpp): A stress test of twenty simultaneous repeat events as well as a couple of interrupts, a stream, and a tick event. For kicks, try changing `NUM_TIMERS` to 200. Program performance will be practically unchanged! + +## Changes between version 2 and 3 + +- Renamed classes from ReactESP to EventLoop and from *Reaction to *Event to better + reflect their purpose. +- Removed the `app` singleton. The user is now responsible for maintaining the EventLoop object. ## Changes between version 1 and 2 ReactESP version 2 has changed the software initialization approach from version 1. Version 1 implemented the Arduino framework standard `setup()` and `loop()` functions behind the scenes, -and a user just instantiated a ReactESP object and provided a setup function as an argument: +and a user just instantiated an EventLoop object and provided a setup function as an argument: ReactESP app([]() { app.onDelay(...); diff --git a/examples/minimal/src/main.cpp b/examples/minimal/src/main.cpp index a1b4021..dbe2b33 100644 --- a/examples/minimal/src/main.cpp +++ b/examples/minimal/src/main.cpp @@ -7,29 +7,29 @@ using namespace reactesp; int led_state = 0; -ReactESP app; +EventLoop event_loop; void setup() { Serial.begin(115200); Serial.println("Starting"); pinMode(LED_PIN, OUTPUT); - - Serial.println("Setting up timed reactions"); + + Serial.println("Setting up timed events"); // toggle LED every 400 ms - app.onRepeat(400, [] () { + event_loop.onRepeat(400, [] () { led_state = !led_state; digitalWrite(LED_PIN, led_state); }); // Additionally, toggle LED every 1020 ms. // This adds an irregularity to the LED blink pattern. - app.onRepeat(1020, [] () { + event_loop.onRepeat(1020, [] () { led_state = !led_state; digitalWrite(LED_PIN, led_state); - }); + }); } void loop() { - app.tick(); + event_loop.tick(); } diff --git a/examples/torture_test/src/main.cpp b/examples/torture_test/src/main.cpp index 77a72fe..4e55236 100644 --- a/examples/torture_test/src/main.cpp +++ b/examples/torture_test/src/main.cpp @@ -14,7 +14,7 @@ using namespace reactesp; int tick_counter = 0; int timer_ticks[NUM_TIMERS]; -ReactESP app; +EventLoop event_loop; void reporter() { Serial.printf("Timer ticks: "); @@ -28,30 +28,30 @@ void reporter() { tick_counter = 0; } -void setup_timers(ReactESP &app) { +void setup_timers(EventLoop &event_loop) { // create twenty timers for (int i=0; iremove(); ire2 = nullptr; } }); - + } -void setup_serial(ReactESP &app) { +void setup_serial(EventLoop &event_loop) { // if something is received on the serial port, turn the led off for one second - app.onAvailable(Serial, [&app] () { - static int reaction_counter = 0; - + event_loop.onAvailable(Serial, [&event_loop] () { + static int event_counter = 0; + Serial.write(Serial.read()); digitalWrite(LED_PIN, HIGH); - reaction_counter++; + event_counter++; - int current = reaction_counter; + int current = event_counter; - app.onDelay(1000, [current] () { - if (reaction_counter==current) { - digitalWrite(LED_PIN, LOW); + event_loop.onDelay(1000, [current] () { + if (event_counter==current) { + digitalWrite(LED_PIN, LOW); } }); }); } -void setup_tick(ReactESP &app) { +void setup_tick(EventLoop &event_loop) { // increase the tick counter on every tick - app.onTick([]() { + event_loop.onTick([]() { tick_counter++; }); } @@ -106,13 +106,13 @@ void setup() { Serial.begin(115200); Serial.println("Starting"); pinMode(LED_PIN, OUTPUT); - - setup_timers(app); - setup_io_pins(app); - setup_serial(app); - setup_tick(app); + + setup_timers(event_loop); + setup_io_pins(event_loop); + setup_serial(event_loop); + setup_tick(event_loop); } void loop() { - app.tick(); + event_loop.tick(); } diff --git a/src/ReactESP.cpp b/src/ReactESP.cpp index 7e50145..53f818c 100644 --- a/src/ReactESP.cpp +++ b/src/ReactESP.cpp @@ -7,47 +7,41 @@ namespace reactesp { -// Reaction classes define the behaviour of each particular -// Reaction +// Event classes define the behaviour of each particular +// Event -bool TimedReaction::operator<(const TimedReaction& other) const { +bool TimedEvent::operator<(const TimedEvent& other) const { return (this->last_trigger_time + this->interval) < (other.last_trigger_time + other.interval); } -void TimedReaction::add(ReactESP* app) { - if (app == nullptr) { - Serial.println("Got a null pointer in TimedReaction::add"); - app = ReactESP::app; - } - app_context = app; - app->timed_queue.push(this); +void TimedEvent::add(EventLoop* event_loop) { + event_loop->timed_queue.push(this); } -void TimedReaction::remove(ReactESP* app) { +void TimedEvent::remove(EventLoop* event_loop) { this->enabled = false; // the object will be deleted when it's popped out of the // timer queue - (void)app; /* unused */ } -DelayReaction::DelayReaction(uint32_t delay, react_callback callback) - : TimedReaction(delay, callback) { +DelayEvent::DelayEvent(uint32_t delay, react_callback callback) + : TimedEvent(delay, callback) { this->last_trigger_time = micros(); } -DelayReaction::DelayReaction(uint64_t delay, react_callback callback) - : TimedReaction(delay, callback) { +DelayEvent::DelayEvent(uint64_t delay, react_callback callback) + : TimedEvent(delay, callback) { this->last_trigger_time = micros(); } -void DelayReaction::tick() { +void DelayEvent::tick(EventLoop* event_loop) { this->last_trigger_time = micros(); this->callback(); delete this; } -void RepeatReaction::tick() { +void RepeatEvent::tick(EventLoop* event_loop) { auto now = micros(); this->last_trigger_time = this->last_trigger_time + this->interval; if (this->last_trigger_time + this->interval < now) { @@ -55,61 +49,46 @@ void RepeatReaction::tick() { this->last_trigger_time = now; } this->callback(); - app_context->timed_queue.push(this); + event_loop->timed_queue.push(this); } -void UntimedReaction::add(ReactESP* app) { - if (app == nullptr) { - app = ReactESP::app; - } - app->untimed_list.push_front(this); +void UntimedEvent::add(EventLoop* event_loop) { + event_loop->untimed_list.push_front(this); } -void UntimedReaction::remove(ReactESP* app) { - if (app == nullptr) { - app = ReactESP::app; - } - - app->untimed_list.remove(this); +void UntimedEvent::remove(EventLoop* event_loop) { + event_loop->untimed_list.remove(this); delete this; } -void StreamReaction::tick() { +void StreamEvent::tick(EventLoop* event_loop) { if (0 != stream.available()) { this->callback(); } } -void TickReaction::tick() { this->callback(); } +void TickEvent::tick(EventLoop* event_loop) { this->callback(); } #ifdef ESP32 -bool ISRReaction::isr_service_installed = false; +bool ISREvent::isr_service_installed = false; -void ISRReaction::isr(void* this_ptr) { - auto* this_ = static_cast(this_ptr); +void ISREvent::isr(void* this_ptr) { + auto* this_ = static_cast(this_ptr); this_->callback(); } #endif -void ISRReaction::add(ReactESP* app) { - if (app == nullptr) { - app = ReactESP::app; - } - +void ISREvent::add(EventLoop* event_loop) { #ifdef ESP32 - gpio_isr_handler_add((gpio_num_t)pin_number, ISRReaction::isr, (void*)this); + gpio_isr_handler_add((gpio_num_t)pin_number, ISREvent::isr, (void*)this); #elif defined(ESP8266) attachInterrupt(digitalPinToInterrupt(pin_number), callback, mode); #endif - app->isr_reaction_list.push_front(this); + event_loop->isr_event_list.push_front(this); } -void ISRReaction::remove(ReactESP* app) { - if (app == nullptr) { - app = ReactESP::app; - } - - app->isr_reaction_list.remove(this); +void ISREvent::remove(EventLoop* event_loop) { + event_loop->isr_event_list.remove(this); #ifdef ESP32 gpio_isr_handler_remove((gpio_num_t)pin_number); #elif defined(ESP8266) @@ -118,12 +97,9 @@ void ISRReaction::remove(ReactESP* app) { delete this; } -// Need to define the static variable outside of the class -ReactESP* ReactESP::app = nullptr; - -void ReactESP::tickTimed() { +void EventLoop::tickTimed() { const uint64_t now = micros(); - TimedReaction* top = nullptr; + TimedEvent* top = nullptr; while (true) { if (timed_queue.empty()) { @@ -139,69 +115,69 @@ void ReactESP::tickTimed() { const uint64_t trigger_t = top->getTriggerTimeMicros(); if (now >= trigger_t) { timed_queue.pop(); - top->tick(); + top->tick(this); } else { break; } } } -void ReactESP::tickUntimed() { - for (UntimedReaction* re : this->untimed_list) { - re->tick(); +void EventLoop::tickUntimed() { + for (UntimedEvent* re : this->untimed_list) { + re->tick(this); } } -void ReactESP::tick() { +void EventLoop::tick() { tickUntimed(); tickTimed(); } -DelayReaction* ReactESP::onDelay(uint32_t delay, react_callback callback) { - auto* dre = new DelayReaction(delay, callback); +DelayEvent* EventLoop::onDelay(uint32_t delay, react_callback callback) { + auto* dre = new DelayEvent(delay, callback); dre->add(this); return dre; } -DelayReaction* ReactESP::onDelayMicros(uint64_t delay, +DelayEvent* EventLoop::onDelayMicros(uint64_t delay, react_callback callback) { - auto* dre = new DelayReaction(delay, callback); + auto* dre = new DelayEvent(delay, callback); dre->add(this); return dre; } -RepeatReaction* ReactESP::onRepeat(uint32_t interval, react_callback callback) { - auto* rre = new RepeatReaction(interval, callback); +RepeatEvent* EventLoop::onRepeat(uint32_t interval, react_callback callback) { + auto* rre = new RepeatEvent(interval, callback); rre->add(this); return rre; } -RepeatReaction* ReactESP::onRepeatMicros(uint64_t interval, +RepeatEvent* EventLoop::onRepeatMicros(uint64_t interval, react_callback callback) { - auto* rre = new RepeatReaction(interval, callback); + auto* rre = new RepeatEvent(interval, callback); rre->add(this); return rre; } -StreamReaction* ReactESP::onAvailable(Stream& stream, react_callback callback) { - auto* sre = new StreamReaction(stream, callback); +StreamEvent* EventLoop::onAvailable(Stream& stream, react_callback callback) { + auto* sre = new StreamEvent(stream, callback); sre->add(this); return sre; } -ISRReaction* ReactESP::onInterrupt(uint8_t pin_number, int mode, +ISREvent* EventLoop::onInterrupt(uint8_t pin_number, int mode, react_callback callback) { - auto* isrre = new ISRReaction(pin_number, mode, callback); + auto* isrre = new ISREvent(pin_number, mode, callback); isrre->add(this); return isrre; } -TickReaction* ReactESP::onTick(react_callback callback) { - auto* tre = new TickReaction(callback); +TickEvent* EventLoop::onTick(react_callback callback) { + auto* tre = new TickEvent(callback); tre->add(this); return tre; } -void ReactESP::remove(Reaction* reaction) { reaction->remove(this); } +void EventLoop::remove(Event* event) { event->remove(this); } } // namespace reactesp diff --git a/src/ReactESP.h b/src/ReactESP.h index 5962497..153b620 100644 --- a/src/ReactESP.h +++ b/src/ReactESP.h @@ -14,82 +14,80 @@ using isr_react_callback = void (*)(void*); // forward declarations -class ReactESP; +class EventLoop; /** - * @brief Reactions are code to be called when a given condition is fulfilled + * @brief EventInterface defines the interface for all events */ -struct ReactionInterface { +struct EventInterface { /** * @brief Default virtual destructor */ - virtual ~ReactionInterface() = default; + virtual ~EventInterface() = default; - virtual void add(ReactESP* app = nullptr) = 0; - virtual void remove(ReactESP* app = nullptr) = 0; - virtual void tick() = 0; + virtual void add(EventLoop* event_loop) = 0; + virtual void remove(EventLoop* event_loop) = 0; + virtual void tick(EventLoop* event_loop) = 0; }; /** - * @brief Reactions are code to be called when a given condition is fulfilled + * @brief Events are code to be called when a given condition is fulfilled */ -class Reaction : public ReactionInterface { +class Event : public EventInterface { protected: const react_callback callback; public: /** - * @brief Construct a new Reaction object + * @brief Construct a new Event object * - * @param callback Function to be called when the reaction is triggered + * @param callback Function to be called when the event is triggered */ - Reaction(react_callback callback) : callback(callback) {} + Event(react_callback callback) : callback(callback) {} // Disabling copy and move semantics - Reaction(const Reaction&) = delete; - Reaction(Reaction&&) = delete; - Reaction& operator=(const Reaction&) = delete; - Reaction& operator=(Reaction&&) = delete; + Event(const Event&) = delete; + Event(Event&&) = delete; + Event& operator=(const Event&) = delete; + Event& operator=(Event&&) = delete; }; /** - * @brief TimedReactions are called based on elapsing of time. + * @brief TimedEvents are called based on elapsing of time. */ -class TimedReaction : public Reaction { +class TimedEvent : public Event { protected: const uint64_t interval; uint64_t last_trigger_time; bool enabled; - // A repeat reaction needs to know which app it belongs to - ReactESP* app_context = nullptr; public: /** - * @brief Construct a new Timed Reaction object + * @brief Construct a new Timed Event object * - * @param interval Interval or delay for the reaction, in milliseconds - * @param callback Function to be called when the reaction is triggered + * @param interval Interval or delay for the event, in milliseconds + * @param callback Function to be called when the event is triggered */ - TimedReaction(uint32_t interval, react_callback callback) - : Reaction(callback), + TimedEvent(uint32_t interval, react_callback callback) + : Event(callback), interval((uint64_t)1000 * (uint64_t)interval), - last_trigger_time(micros64()), + last_trigger_time(micros()), enabled(true) {} /** - * @brief Construct a new Timed Reaction object + * @brief Construct a new Timed Event object * * @param interval Interval, in microseconds - * @param callback Function to be called when the reaction is triggered + * @param callback Function to be called when the event is triggered */ - TimedReaction(uint64_t interval, react_callback callback) - : Reaction(callback), + TimedEvent(uint64_t interval, react_callback callback) + : Event(callback), interval(interval), last_trigger_time(micros()), enabled(true) {} - bool operator<(const TimedReaction& other) const; - void add(ReactESP* app = nullptr) override; - void remove(ReactESP* app = nullptr) override; + bool operator<(const TimedEvent& other) const; + void add(EventLoop* event_loop) override; + void remove(EventLoop* event_loop) override; uint32_t getTriggerTime() const { return (last_trigger_time + interval) / 1000; } @@ -100,108 +98,108 @@ class TimedReaction : public Reaction { }; struct TriggerTimeCompare { - bool operator()(TimedReaction* a, TimedReaction* b) { return *b < *a; } + bool operator()(TimedEvent* a, TimedEvent* b) { return *b < *a; } }; /** - * @brief Reaction that is triggered after a certain time delay + * @brief Event that is triggered after a certain time delay */ -class DelayReaction : public TimedReaction { +class DelayEvent : public TimedEvent { public: /** - * @brief Construct a new Delay Reaction object + * @brief Construct a new Delay Event object * * @param delay Delay, in milliseconds * @param callback Function to be called after the delay */ - DelayReaction(uint32_t delay, react_callback callback); + DelayEvent(uint32_t delay, react_callback callback); /** - * @brief Construct a new Delay Reaction object + * @brief Construct a new Delay Event object * * @param delay Delay, in microseconds * @param callback Function to be called after the delay */ - DelayReaction(uint64_t delay, react_callback callback); + DelayEvent(uint64_t delay, react_callback callback); - void tick() override; + void tick(EventLoop* event_loop) override; }; /** - * @brief Reaction that is triggered repeatedly + * @brief Event that is triggered repeatedly */ -class RepeatReaction : public TimedReaction { +class RepeatEvent : public TimedEvent { public: /** - * @brief Construct a new Repeat Reaction object + * @brief Construct a new Repeat Event object * * @param interval Repetition interval, in milliseconds * @param callback Function to be called at every repetition */ - RepeatReaction(uint32_t interval, react_callback callback) - : TimedReaction(interval, callback) {} + RepeatEvent(uint32_t interval, react_callback callback) + : TimedEvent(interval, callback) {} /** - * @brief Construct a new Repeat Reaction object + * @brief Construct a new Repeat Event object * * @param interval Repetition interval, in microseconds * @param callback Function to be called at every repetition */ - RepeatReaction(uint64_t interval, react_callback callback) - : TimedReaction(interval, callback) {} + RepeatEvent(uint64_t interval, react_callback callback) + : TimedEvent(interval, callback) {} - void tick() override; + void tick(EventLoop* event_loop) override; }; /** - * @brief Reactions that are triggered based on something else than time + * @brief Events that are triggered based on something else than time */ -class UntimedReaction : public Reaction { +class UntimedEvent : public Event { public: - UntimedReaction(react_callback callback) : Reaction(callback) {} + UntimedEvent(react_callback callback) : Event(callback) {} - void add(ReactESP* app = nullptr) override; - void remove(ReactESP* app = nullptr) override; + void add(EventLoop* event_loop) override; + void remove(EventLoop* event_loop) override; }; /** - * @brief Reaction that is triggered when there is input available at the given + * @brief Event that is triggered when there is input available at the given * Arduino Stream */ -class StreamReaction : public UntimedReaction { +class StreamEvent : public UntimedEvent { private: Stream& stream; public: /** - * @brief Construct a new Stream Reaction object + * @brief Construct a new Stream Event object * * @param stream Stream to monitor * @param callback Callback to call for new input */ - StreamReaction(Stream& stream, react_callback callback) - : UntimedReaction(callback), stream(stream) {} + StreamEvent(Stream& stream, react_callback callback) + : UntimedEvent(callback), stream(stream) {} - void tick() override; + void tick(EventLoop* event_loop) override; }; /** - * @brief Reaction that is triggered unconditionally at each execution loop + * @brief Event that is triggered unconditionally at each execution loop */ -class TickReaction : public UntimedReaction { +class TickEvent : public UntimedEvent { public: /** - * @brief Construct a new Tick Reaction object + * @brief Construct a new Tick Event object * * @param callback Function to be called at each execution loop */ - TickReaction(react_callback callback) : UntimedReaction(callback) {} + TickEvent(react_callback callback) : UntimedEvent(callback) {} - void tick() override; + void tick(EventLoop* event_loop) override; }; /** - * @brief Reaction that is triggered on an input pin change + * @brief Event that is triggered on an input pin change */ -class ISRReaction : public Reaction { +class ISREvent : public Event { private: const uint8_t pin_number; const int mode; @@ -213,15 +211,15 @@ class ISRReaction : public Reaction { public: /** - * @brief Construct a new ISRReaction object + * @brief Construct a new ISREvent object * * @param pin_number GPIO pin number to which the interrupt is attached * @param mode Interrupt mode. One of RISING, FALLING, CHANGE * @param callback Interrupt callback. Keep this function short and add the * ICACHE_RAM_ATTR attribute. */ - ISRReaction(uint8_t pin_number, int mode, react_callback callback) - : Reaction(callback), pin_number(pin_number), mode(mode) { + ISREvent(uint8_t pin_number, int mode, react_callback callback) + : Event(callback), pin_number(pin_number), mode(mode) { #ifdef ESP32 gpio_int_type_t intr_type; switch (mode) { @@ -248,127 +246,119 @@ class ISRReaction : public Reaction { #endif } - void add(ReactESP* app = nullptr) override; - void remove(ReactESP* app = nullptr) override; - void tick() override {} + void add(EventLoop* event_loop) override; + void remove(EventLoop* event_loop) override; + void tick(EventLoop* event_loop) override {} }; /////////////////////////////////////// -// ReactESP main class implementation +// EventLoop main event loop implementation /** - * @brief Main class of a ReactESP program + * @brief Main event loop of a EventLoop program */ -class ReactESP { - friend class Reaction; - friend class TimedReaction; - friend class RepeatReaction; - friend class UntimedReaction; - friend class ISRReaction; +class EventLoop { + friend class Event; + friend class TimedEvent; + friend class RepeatEvent; + friend class UntimedEvent; + friend class ISREvent; public: /** - * @brief Construct a new ReactESP object. - * - * @param singleton If true, set the singleton instance to this object + * @brief Construct a new EventLoop object. */ - ReactESP(bool singleton = true) - : timed_queue(), untimed_list(), isr_reaction_list(), isr_pending_list() { - if (singleton) { - app = this; - } + EventLoop() + : timed_queue(), untimed_list(), isr_event_list(), isr_pending_list() { } // Disabling copy and move semantics - ReactESP(const ReactESP&) = delete; - ReactESP(ReactESP&&) = delete; - ReactESP& operator=(const ReactESP&) = delete; - ReactESP& operator=(ReactESP&&) = delete; + EventLoop(const EventLoop&) = delete; + EventLoop(EventLoop&&) = delete; + EventLoop& operator=(const EventLoop&) = delete; + EventLoop& operator=(EventLoop&&) = delete; void tick(); - /// Static singleton reference to the instantiated ReactESP object - static ReactESP* app; - /** - * @brief Create a new DelayReaction + * @brief Create a new DelayEvent * * @param delay Delay, in milliseconds * @param callback Callback function - * @return DelayReaction* + * @return DelayEvent* */ - DelayReaction* onDelay(uint32_t delay, react_callback callback); + DelayEvent* onDelay(uint32_t delay, react_callback callback); /** - * @brief Create a new DelayReaction + * @brief Create a new DelayEvent * * @param delay Delay, in microseconds * @param callback Callback function - * @return DelayReaction* + * @return DelayEvent* */ - DelayReaction* onDelayMicros(uint64_t delay, react_callback callback); + DelayEvent* onDelayMicros(uint64_t delay, react_callback callback); /** - * @brief Create a new RepeatReaction + * @brief Create a new RepeatEvent * * @param delay Interval, in milliseconds * @param callback Callback function - * @return RepeatReaction* + * @return RepeatEvent* */ - RepeatReaction* onRepeat(uint32_t interval, react_callback callback); + RepeatEvent* onRepeat(uint32_t interval, react_callback callback); /** - * @brief Create a new RepeatReaction + * @brief Create a new RepeatEvent * * @param delay Interval, in microseconds * @param callback Callback function - * @return RepeatReaction* + * @return RepeatEvent* */ - RepeatReaction* onRepeatMicros(uint64_t interval, react_callback callback); + RepeatEvent* onRepeatMicros(uint64_t interval, react_callback callback); /** - * @brief Create a new StreamReaction + * @brief Create a new StreamEvent * * @param stream Arduino Stream object to monitor * @param callback Callback function - * @return StreamReaction* + * @return StreamEvent* */ - StreamReaction* onAvailable(Stream& stream, react_callback callback); + StreamEvent* onAvailable(Stream& stream, react_callback callback); /** - * @brief Create a new ISRReaction (interrupt reaction) + * @brief Create a new ISREvent (interrupt event) * * @param pin_number GPIO pin number * @param mode One of CHANGE, RISING, FALLING * @param callback Interrupt handler to call. This should be a very simple * function, ideally setting a flag variable or incrementing a counter. The * function should be defined with ICACHE_RAM_ATTR. - * @return ISRReaction* + * @return ISREvent* */ - ISRReaction* onInterrupt(uint8_t pin_number, int mode, + ISREvent* onInterrupt(uint8_t pin_number, int mode, react_callback callback); /** - * @brief Create a new TickReaction + * @brief Create a new TickEvent * * @param callback Callback function to be called at every loop execution - * @return TickReaction* + * @return TickEvent* */ - TickReaction* onTick(react_callback callback); + TickEvent* onTick(react_callback callback); /** - * @brief Remove a reaction from the list of active reactions + * @brief Remove a event from the list of active events * - * @param reaction Reaction to remove + * @param event Event to remove */ - void remove(Reaction* reaction); + void remove(Event* event); private: - std::priority_queue, + std::priority_queue, TriggerTimeCompare> timed_queue; - std::forward_list untimed_list; - std::forward_list isr_reaction_list; - std::forward_list isr_pending_list; + std::forward_list untimed_list; + std::forward_list isr_event_list; + std::forward_list isr_pending_list; void tickTimed(); void tickUntimed(); void tickISR(); - void add(Reaction* re); + void add(Event* re); }; } // namespace reactesp