214 lines
6.1 KiB
C++
214 lines
6.1 KiB
C++
#include "HistoryStore.hpp"
|
|
#include "esp_log.h"
|
|
#include <cstdio>
|
|
#include <ctime>
|
|
#include <cmath>
|
|
|
|
static const char* TAG = "HistoryStore";
|
|
|
|
// HistoryStore is effectively disabled to save resources and prevent hangs.
|
|
// It now acts as a simple pass-through for the latest value (for real-time chart indication)
|
|
// but does not record history or write to SD card.
|
|
|
|
HistoryStore& HistoryStore::instance() {
|
|
static HistoryStore inst;
|
|
return inst;
|
|
}
|
|
|
|
HistoryStore::HistoryStore() {
|
|
mutex_ = xSemaphoreCreateMutex();
|
|
}
|
|
|
|
HistoryStore::~HistoryStore() {
|
|
if (mutex_) {
|
|
vSemaphoreDelete(mutex_);
|
|
}
|
|
}
|
|
|
|
bool HistoryStore::isNumericSource(TextSource source) {
|
|
return source == TextSource::KNX_DPT_TEMP ||
|
|
source == TextSource::KNX_DPT_PERCENT ||
|
|
source == TextSource::KNX_DPT_POWER ||
|
|
source == TextSource::KNX_DPT_ENERGY ||
|
|
source == TextSource::KNX_DPT_DECIMALFACTOR;
|
|
}
|
|
|
|
bool HistoryStore::keysEqual(const SeriesKey& a, const SeriesKey& b) {
|
|
return a.addr == b.addr && a.source == b.source;
|
|
}
|
|
|
|
HistoryStore::HistorySeries* HistoryStore::findSeries(uint16_t groupAddr, TextSource source) {
|
|
SeriesKey key{groupAddr, source};
|
|
for (size_t i = 0; i < series_.size(); ++i) {
|
|
if (series_[i].active && keysEqual(series_[i].key, key)) {
|
|
return &series_[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const HistoryStore::HistorySeries* HistoryStore::findSeries(uint16_t groupAddr, TextSource source) const {
|
|
SeriesKey key{groupAddr, source};
|
|
for (size_t i = 0; i < series_.size(); ++i) {
|
|
if (series_[i].active && keysEqual(series_[i].key, key)) {
|
|
return &series_[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
HistoryStore::HistorySeries* HistoryStore::findSeriesByKey(const SeriesKey& key) {
|
|
for (size_t i = 0; i < series_.size(); ++i) {
|
|
if (keysEqual(series_[i].key, key)) {
|
|
return &series_[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const HistoryStore::HistorySeries* HistoryStore::findSeriesByKey(const SeriesKey& key) const {
|
|
for (size_t i = 0; i < series_.size(); ++i) {
|
|
if (keysEqual(series_[i].key, key)) {
|
|
return &series_[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void HistoryStore::configureFromConfig(const GuiConfig& config) {
|
|
xSemaphoreTake(mutex_, portMAX_DELAY);
|
|
std::array<SeriesKey, HISTORY_MAX_SERIES> needed = {};
|
|
size_t neededCount = 0;
|
|
|
|
auto addSeries = [&](uint16_t addr, TextSource source) {
|
|
if (addr == 0 || !isNumericSource(source)) return;
|
|
SeriesKey key{addr, source};
|
|
for (size_t i = 0; i < neededCount; ++i) {
|
|
if (keysEqual(needed[i], key)) return;
|
|
}
|
|
if (neededCount >= HISTORY_MAX_SERIES) return;
|
|
needed[neededCount++] = key;
|
|
};
|
|
|
|
for (size_t s = 0; s < config.screenCount; ++s) {
|
|
const ScreenConfig& screen = config.screens[s];
|
|
for (size_t i = 0; i < screen.widgetCount; ++i) {
|
|
const WidgetConfig& w = screen.widgets[i];
|
|
if (w.type != WidgetType::CHART) continue;
|
|
uint8_t count = w.chartSeriesCount;
|
|
if (count > CHART_MAX_SERIES) count = CHART_MAX_SERIES;
|
|
for (uint8_t si = 0; si < count; ++si) {
|
|
addSeries(w.chartKnxAddress[si], w.chartTextSource[si]);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::array<bool, HISTORY_MAX_SERIES> keep = {};
|
|
for (size_t i = 0; i < neededCount; ++i) {
|
|
const SeriesKey& key = needed[i];
|
|
HistorySeries* existing = findSeriesByKey(key);
|
|
if (existing) {
|
|
size_t idx = static_cast<size_t>(existing - series_.data());
|
|
keep[idx] = true;
|
|
continue;
|
|
}
|
|
// Find empty slot
|
|
HistorySeries* slot = nullptr;
|
|
for (size_t si = 0; si < series_.size(); ++si) {
|
|
if (!keep[si] && !series_[si].active) {
|
|
slot = &series_[si];
|
|
keep[si] = true;
|
|
break;
|
|
}
|
|
}
|
|
if (slot) {
|
|
slot->key = key;
|
|
slot->active = true;
|
|
slot->hasLatest = false;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < series_.size(); ++i) {
|
|
series_[i].active = keep[i];
|
|
}
|
|
xSemaphoreGive(mutex_);
|
|
}
|
|
|
|
bool HistoryStore::isTracked(uint16_t groupAddr, TextSource source) const {
|
|
xSemaphoreTake(mutex_, portMAX_DELAY);
|
|
bool tracked = findSeries(groupAddr, source) != nullptr;
|
|
xSemaphoreGive(mutex_);
|
|
return tracked;
|
|
}
|
|
|
|
bool HistoryStore::updateLatest(uint16_t groupAddr, TextSource source, float value) {
|
|
xSemaphoreTake(mutex_, portMAX_DELAY);
|
|
HistorySeries* series = findSeries(groupAddr, source);
|
|
if (!series) {
|
|
xSemaphoreGive(mutex_);
|
|
return false;
|
|
}
|
|
series->latestValue = value;
|
|
series->hasLatest = true;
|
|
xSemaphoreGive(mutex_);
|
|
return true;
|
|
}
|
|
|
|
int64_t HistoryStore::now() const {
|
|
return (int64_t)time(nullptr);
|
|
}
|
|
|
|
int32_t HistoryStore::periodSeconds(ChartPeriod period) const {
|
|
return 3600; // Dummy
|
|
}
|
|
|
|
bool HistoryStore::fillChartSeries(uint16_t groupAddr, TextSource source, ChartPeriod period,
|
|
int32_t* outValues, size_t outCount) const {
|
|
if (!outValues || outCount == 0) return false;
|
|
|
|
// Just return the latest value repeated, effectively a flat line
|
|
xSemaphoreTake(mutex_, portMAX_DELAY);
|
|
const HistorySeries* series = findSeries(groupAddr, source);
|
|
int32_t val = NO_POINT;
|
|
if (series && series->hasLatest) {
|
|
val = static_cast<int32_t>(lrintf(series->latestValue));
|
|
}
|
|
xSemaphoreGive(mutex_);
|
|
|
|
for (size_t i = 0; i < outCount; ++i) {
|
|
outValues[i] = val;
|
|
}
|
|
return (val != NO_POINT);
|
|
}
|
|
|
|
bool HistoryStore::tick() {
|
|
return false; // Disabled
|
|
}
|
|
|
|
void HistoryStore::performAutoSave() {
|
|
// Disabled
|
|
}
|
|
|
|
void HistoryStore::updateTimeOfDay(const tm& value) {
|
|
// Disabled
|
|
}
|
|
|
|
void HistoryStore::updateDate(const tm& value) {
|
|
// Disabled
|
|
}
|
|
|
|
void HistoryStore::updateDateTime(const tm& value) {
|
|
// Disabled
|
|
}
|
|
|
|
void HistoryStore::clearAll() {
|
|
// Disabled
|
|
}
|
|
|
|
void HistoryStore::saveToSdCard() {
|
|
// Disabled
|
|
}
|
|
|
|
void HistoryStore::loadFromSdCard() {
|
|
// Disabled
|
|
} |