knxdisplay/main/HistoryStore.cpp
2026-01-30 19:07:52 +01:00

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
}