119 lines
3.3 KiB
C++
119 lines
3.3 KiB
C++
#pragma once
|
|
|
|
#include "WidgetConfig.hpp"
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/semphr.h"
|
|
#include <array>
|
|
#include <climits>
|
|
#include <cstdint>
|
|
#include <ctime>
|
|
|
|
class HistoryStore {
|
|
public:
|
|
static HistoryStore& instance();
|
|
|
|
void configureFromConfig(const GuiConfig& config);
|
|
bool updateLatest(uint16_t groupAddr, TextSource source, float value);
|
|
bool isTracked(uint16_t groupAddr, TextSource source) const;
|
|
bool tick();
|
|
void performAutoSave();
|
|
|
|
bool fillChartSeries(uint16_t groupAddr, TextSource source, ChartPeriod period,
|
|
int32_t* outValues, size_t outCount) const;
|
|
|
|
int64_t now() const;
|
|
bool isTimeSynced() const { return now() > 1577836800LL; }
|
|
|
|
void updateTimeOfDay(const tm& value);
|
|
void updateDate(const tm& value);
|
|
void updateDateTime(const tm& value);
|
|
void clearAll();
|
|
|
|
void loadFromSdCard();
|
|
void saveToSdCard();
|
|
|
|
static constexpr size_t CHART_POINT_COUNT = 120;
|
|
static constexpr int32_t NO_POINT = INT32_MAX;
|
|
|
|
private:
|
|
HistoryStore();
|
|
~HistoryStore();
|
|
|
|
struct SeriesKey {
|
|
uint16_t addr = 0;
|
|
TextSource source = TextSource::STATIC;
|
|
};
|
|
|
|
struct HistoryPoint {
|
|
int32_t ts = 0;
|
|
float value = 0.0f;
|
|
};
|
|
|
|
template <size_t N>
|
|
struct RingBuffer {
|
|
std::array<HistoryPoint, N> points = {};
|
|
size_t count = 0;
|
|
size_t head = 0;
|
|
|
|
void clear() {
|
|
count = 0;
|
|
head = 0;
|
|
}
|
|
|
|
void push(const HistoryPoint& point) {
|
|
points[head] = point;
|
|
head = (head + 1) % N;
|
|
if (count < N) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
template <typename Fn>
|
|
void forEach(Fn&& fn) const {
|
|
if (count == 0) return;
|
|
size_t start = (head + N - count) % N;
|
|
for (size_t i = 0; i < count; ++i) {
|
|
const HistoryPoint& p = points[(start + i) % N];
|
|
fn(p);
|
|
}
|
|
}
|
|
};
|
|
|
|
static constexpr size_t HISTORY_MAX_SERIES = 4;
|
|
static constexpr size_t HISTORY_FINE_CAP = 360;
|
|
static constexpr size_t HISTORY_COARSE_CAP = 360;
|
|
static constexpr int32_t HISTORY_FINE_INTERVAL = 120;
|
|
static constexpr int32_t HISTORY_COARSE_INTERVAL = 3600;
|
|
static constexpr int32_t HISTORY_MONTH_SECONDS = 30 * 24 * 3600;
|
|
|
|
struct HistorySeries {
|
|
SeriesKey key;
|
|
bool active = false;
|
|
bool hasLatest = false;
|
|
float latestValue = 0.0f;
|
|
int32_t latestTs = 0;
|
|
RingBuffer<HISTORY_FINE_CAP> fine;
|
|
RingBuffer<HISTORY_COARSE_CAP> coarse;
|
|
int32_t lastFineSampleTs = 0;
|
|
int32_t lastCoarseSampleTs = 0;
|
|
};
|
|
|
|
static bool isNumericSource(TextSource source);
|
|
static bool keysEqual(const SeriesKey& a, const SeriesKey& b);
|
|
|
|
int32_t periodSeconds(ChartPeriod period) const;
|
|
|
|
HistorySeries* findSeries(uint16_t groupAddr, TextSource source);
|
|
const HistorySeries* findSeries(uint16_t groupAddr, TextSource source) const;
|
|
HistorySeries* findSeriesByKey(const SeriesKey& key);
|
|
const HistorySeries* findSeriesByKey(const SeriesKey& key) const;
|
|
|
|
std::array<HistorySeries, HISTORY_MAX_SERIES> series_ = {};
|
|
size_t seriesCount_ = 0;
|
|
|
|
bool dirty_ = false;
|
|
int64_t lastSaveMonoUs_ = 0;
|
|
|
|
mutable SemaphoreHandle_t mutex_ = nullptr;
|
|
};
|