Fist commit

This commit is contained in:
Angel Ivan
2026-10-02 12:08:21 -06:00
commit 2a9eabce14
54 changed files with 10737 additions and 0 deletions

151
lib/timbre_core/PatternEngine.h Executable file
View File

@@ -0,0 +1,151 @@
#pragma once
#include "Model.h"
#include "ScheduleValidator.h"
#include "SessionClock.h"
#include <array>
#include <cstdint>
#include <functional>
#include <utility>
namespace timbre_core {
class PatternEngine {
public:
using OutputCallback = std::function<void(RelayId, bool)>;
using ClockCallback = std::function<std::uint64_t()>;
using SimpleOutputCallback = std::function<void(bool)>;
struct Request {
Pattern pattern{};
std::uint64_t startAtMs = 0;
};
static constexpr std::size_t kQueueCapacity = kPatternQueueCapacity;
static constexpr std::uint8_t kNoPhase = 0xFFu;
static constexpr std::uint64_t kMillisecondsPerSecond = 1000ULL;
PatternEngine() {
initializeDefaults();
}
explicit PatternEngine(OutputCallback output) : output_(std::move(output)) {
initializeDefaults();
}
explicit PatternEngine(ClockCallback clock, OutputCallback output = OutputCallback())
: clock_(std::move(clock)), output_(std::move(output)) {
initializeDefaults();
}
explicit PatternEngine(MonotonicClock& clock, OutputCallback output = OutputCallback())
: clockInterface_(&clock), output_(std::move(output)) {
initializeDefaults();
}
explicit PatternEngine(const AppState& state, OutputCallback output = OutputCallback())
: output_(std::move(output)) {
initializeDefaults();
setPatterns(state);
}
void setClock(ClockCallback clock) {
clockInterface_ = nullptr;
clock_ = std::move(clock);
}
void setClock(MonotonicClock& clock) {
clock_ = ClockCallback();
clockInterface_ = &clock;
}
void setOutput(OutputCallback output) {
output_ = std::move(output);
simpleOutput_ = SimpleOutputCallback();
}
void setSimpleOutput(SimpleOutputCallback output) {
simpleOutput_ = std::move(output);
output_ = OutputCallback();
}
void setPattern(const Pattern& pattern) noexcept;
void setPatterns(const AppState& state) noexcept;
void setState(const AppState& state) noexcept { setPatterns(state); }
void configure(const AppState& state) noexcept { setPatterns(state); }
// Queueing is non-blocking and bounded. A request is a snapshot, so a
// later configuration commit cannot mutate an already queued sequence.
bool enqueue(PatternId id);
bool enqueue(PatternId id, std::uint64_t startAtMs);
bool enqueue(const Pattern& pattern);
bool enqueue(const Pattern& pattern, std::uint64_t startAtMs);
bool enqueue(PatternId id, const AppState& state, std::uint64_t startAtMs);
bool enqueue(PatternId id, const AppState& state);
bool start(PatternId id, std::uint64_t nowMs);
bool start(const Pattern& pattern, std::uint64_t nowMs);
bool start(PatternId id, const AppState& state, std::uint64_t nowMs);
bool start(PatternId id, const AppState& state);
// Returns true when the state/output changed. It performs no waiting and
// consumes at most one bounded transition burst (six phases).
bool update(std::uint64_t nowMs);
bool update();
bool tick(std::uint64_t nowMs) { return update(nowMs); }
bool tick() { return update(); }
// Stops the active sequence and guarantees an OFF output. Pending queue
// entries are retained; cancelAll() removes them as well.
void stop(std::uint64_t nowMs);
void stop();
void finish() { stop(); }
void cancelAll() noexcept;
bool running() const noexcept { return active_; }
bool outputOn() const noexcept { return outputOn_; }
PatternId activePatternId() const noexcept { return activePattern_.id; }
const Pattern* activePattern() const noexcept { return active_ ? &activePattern_ : nullptr; }
std::uint8_t activePhase() const noexcept { return active_ ? phase_ : kNoPhase; }
std::uint64_t deadlineMs() const noexcept { return active_ ? deadlineMs_ : 0; }
std::uint64_t deadline() const noexcept { return deadlineMs(); }
std::uint64_t nextDeadlineMs() const noexcept { return deadlineMs(); }
std::size_t queueSize() const noexcept { return queueSize_; }
std::size_t queueCapacity() const noexcept { return kQueueCapacity; }
bool configured(PatternId id) const noexcept {
const std::size_t index = patternIndex(id);
return index < kMaxPatterns && (configuredPatternMask_ & (1u << index)) != 0;
}
RelayId lastOutputRelay() const noexcept { return lastOutputRelay_; }
bool reservedRelayDriven() const noexcept { return reservedRelayDriven_; }
private:
void initializeDefaults() noexcept;
std::uint64_t readClock() const;
bool validPattern(const Pattern& pattern) const noexcept;
bool enqueueSnapshot(const Pattern& pattern, std::uint64_t startAtMs);
bool startSnapshot(const Pattern& pattern, std::uint64_t nowMs);
void pump(std::uint64_t nowMs);
void finishActive() noexcept;
void emit(bool on) noexcept;
static std::uint64_t addDuration(std::uint64_t deadline, std::uint8_t seconds) noexcept;
ClockCallback clock_;
MonotonicClock* clockInterface_ = nullptr;
OutputCallback output_;
SimpleOutputCallback simpleOutput_;
std::array<Pattern, kMaxPatterns> patterns_{};
std::uint8_t configuredPatternMask_ =
static_cast<std::uint8_t>((1u << kMaxPatterns) - 1u);
std::array<Request, kQueueCapacity> queue_{};
std::size_t queueHead_ = 0;
std::size_t queueSize_ = 0;
Pattern activePattern_{};
bool active_ = false;
std::uint8_t phase_ = kNoPhase;
std::uint64_t deadlineMs_ = 0;
std::uint64_t lastUpdateMs_ = 0;
bool hasUpdateTime_ = false;
bool outputOn_ = false;
RelayId lastOutputRelay_ = RelayId::Main;
bool reservedRelayDriven_ = false;
};
} // namespace timbre_core