mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 22:02:17 +01:00
A full event ring threw an outcome away, breaking the rule that every job we submit ends with exactly one of finished, aborted or dropped. A lost outcome left a favorite uncommitted and a held shade waiting out its 30s timeout. submit() and hasRoom() now keep room for two outcomes of every job not yet ended, so an event can't be lost; if loop() stops collecting them, new jobs are refused instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
78 lines
3.6 KiB
C++
78 lines
3.6 KiB
C++
#ifndef RADIO_SCHEDULER_H
|
|
#define RADIO_SCHEDULER_H
|
|
#include <stdint.h>
|
|
#include "TxBuffer.h"
|
|
|
|
// Reasons the radio must not transmit. TX resumes once every reason is cleared.
|
|
#define TX_SUSPEND_SCAN 0x01
|
|
#define TX_SUSPEND_OTA 0x02
|
|
// No job is waiting so the radio task sleeps until it is woken.
|
|
#define RADIO_WAIT_FOREVER 0xFFFFFFFFu
|
|
|
|
// The radio jobs waiting for, or on, the air (see GLOSSARY.md). It decides
|
|
// their order, when a delayed job is due, what a flush or suspend drops, and
|
|
// reports the outcome of every job. Kept free of FreeRTOS and the radio so it
|
|
// can be unit tested on the PC (pio test -e native).
|
|
//
|
|
// Not thread safe. The caller serializes every call and passes the time in.
|
|
class RadioScheduler {
|
|
public:
|
|
// Queues a job. Its ticket comes back and is also set in job.seq, 0 when
|
|
// the queue is full or suspended, or when outcomes aren't collected with
|
|
// popEvent() fast enough to leave room for its own. A priority job (a stop) may use the
|
|
// reserved slots and goes ahead of every job queued at the back, unless a
|
|
// job of its remote is already waiting: overtaking that would put the
|
|
// rolling codes out of order. A delayed job doesn't go on air until
|
|
// delayMs after it was queued and after the job ahead of it ended, and holds
|
|
// up the jobs queued behind it.
|
|
uint32_t submit(somfy_tx_job_t &job, bool priority, uint16_t delayMs, uint32_t now);
|
|
// Whether a job would be taken right now.
|
|
bool hasRoom(bool priority) const;
|
|
// For the radio task: takes the job to send next. False when there is
|
|
// none due, waitMs then says how long until there is (RADIO_WAIT_FOREVER
|
|
// when nothing waits). A job taken goes on air through start() and
|
|
// ended().
|
|
bool next(uint32_t now, somfy_tx_job_t &job, uint32_t &waitMs);
|
|
// The job taken is about to go on air. False when it was flushed since,
|
|
// it is then dropped and must not be sent.
|
|
bool start(uint32_t ticket, uint32_t now);
|
|
// Whether the job on air should send its next frame. A flush cuts it
|
|
// short.
|
|
bool keepSending(uint32_t ticket) const;
|
|
// The job on air stopped, after every frame when complete.
|
|
void ended(uint32_t ticket, bool complete, uint32_t now);
|
|
// Drops every waiting job and cuts short the one on air.
|
|
void flush(uint32_t now);
|
|
// Refuses new jobs until every reason (TX_SUSPEND_*) is resumed. Flushes.
|
|
void suspend(uint8_t reason, uint32_t now);
|
|
void resume(uint8_t reason);
|
|
bool suspended() const { return this->suspendMask != 0; }
|
|
// Takes the oldest job outcome not yet handed out. Every job submitted
|
|
// that isn't a repeater frame is started and then finished or aborted, or
|
|
// dropped.
|
|
bool popEvent(somfy_tx_event_t &evt);
|
|
private:
|
|
somfy_tx_job_t jobs[TX_JOB_QUEUE_SIZE];
|
|
uint8_t count = 0;
|
|
uint32_t lastSeq = 0;
|
|
uint8_t suspendMask = 0;
|
|
// The job taken by next() and not yet ended (0 for none), kept for its
|
|
// events.
|
|
uint32_t taken = 0;
|
|
somfy_tx_job_t takenJob;
|
|
bool takenFlushed = false;
|
|
// When a job last went off air, for the delay of the job after it.
|
|
uint32_t lastEnded = 0;
|
|
bool endedOnce = false;
|
|
somfy_tx_event_t events[TX_JOB_QUEUE_SIZE * 3];
|
|
uint8_t eventHead = 0;
|
|
uint8_t eventCount = 0;
|
|
// Whether the outcomes of every job not yet ended, and of one more, still
|
|
// fit in events. A job is refused otherwise so no outcome is ever lost.
|
|
bool outcomesFit() const;
|
|
// Whether a job of the remote is still waiting.
|
|
bool waitingFor(uint32_t remoteAddress) const;
|
|
void remove(uint8_t i);
|
|
void post(tx_event_t type, const somfy_tx_job_t &job, uint32_t at);
|
|
};
|
|
#endif
|