Fist commit
This commit is contained in:
151
lib/timbre_core/PatternEngine.h
Executable file
151
lib/timbre_core/PatternEngine.h
Executable 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
|
||||
Reference in New Issue
Block a user