209 lines
6.9 KiB
C++
209 lines
6.9 KiB
C++
#include "ChartWidget.hpp"
|
|
#include "../Fonts.hpp"
|
|
#include "lvgl.h"
|
|
#include <algorithm>
|
|
|
|
ChartWidget::ChartWidget(const WidgetConfig& config)
|
|
: Widget(config) {
|
|
}
|
|
|
|
lv_obj_t* ChartWidget::create(lv_obj_t* parent) {
|
|
obj_ = lv_obj_create(parent);
|
|
if (!obj_) return nullptr;
|
|
lv_obj_remove_style_all(obj_);
|
|
|
|
int32_t width = config_.width > 0 ? config_.width : 240;
|
|
int32_t height = config_.height > 0 ? config_.height : 160;
|
|
lv_obj_set_pos(obj_, config_.x, config_.y);
|
|
lv_obj_set_size(obj_, width, height);
|
|
lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE);
|
|
|
|
const int32_t yAxisWidth = 48;
|
|
const int32_t xAxisHeight = 26;
|
|
int32_t chartWidth = width - yAxisWidth;
|
|
int32_t chartHeight = height - xAxisHeight;
|
|
if (chartWidth < 20) chartWidth = 20;
|
|
if (chartHeight < 20) chartHeight = 20;
|
|
|
|
yScale_ = lv_scale_create(obj_);
|
|
if (yScale_) {
|
|
lv_scale_set_mode(yScale_, LV_SCALE_MODE_VERTICAL_LEFT);
|
|
lv_scale_set_total_tick_count(yScale_, 5);
|
|
lv_scale_set_major_tick_every(yScale_, 1);
|
|
lv_scale_set_label_show(yScale_, true);
|
|
lv_scale_set_range(yScale_, 0, 100);
|
|
lv_obj_set_pos(yScale_, 0, 0);
|
|
lv_obj_set_size(yScale_, yAxisWidth, chartHeight);
|
|
}
|
|
|
|
xScale_ = lv_scale_create(obj_);
|
|
if (xScale_) {
|
|
lv_scale_set_mode(xScale_, LV_SCALE_MODE_HORIZONTAL_BOTTOM);
|
|
lv_scale_set_label_show(xScale_, true);
|
|
lv_obj_set_pos(xScale_, yAxisWidth, chartHeight);
|
|
lv_obj_set_size(xScale_, chartWidth, xAxisHeight);
|
|
}
|
|
|
|
chart_ = lv_chart_create(obj_);
|
|
if (!chart_) {
|
|
return obj_;
|
|
}
|
|
lv_obj_set_pos(chart_, yAxisWidth, 0);
|
|
lv_obj_set_size(chart_, chartWidth, chartHeight);
|
|
lv_obj_clear_flag(chart_, LV_OBJ_FLAG_SCROLLABLE);
|
|
|
|
lv_chart_set_type(chart_, LV_CHART_TYPE_LINE);
|
|
lv_chart_set_point_count(chart_, HistoryStore::CHART_POINT_COUNT);
|
|
lv_chart_set_div_line_count(chart_, 4, 6);
|
|
|
|
uint8_t count = config_.chartSeriesCount;
|
|
if (count > CHART_MAX_SERIES) count = CHART_MAX_SERIES;
|
|
|
|
for (uint8_t i = 0; i < count; ++i) {
|
|
lv_color_t color = lv_color_make(
|
|
config_.chartSeriesColor[i].r,
|
|
config_.chartSeriesColor[i].g,
|
|
config_.chartSeriesColor[i].b);
|
|
series_[i] = lv_chart_add_series(chart_, color, LV_CHART_AXIS_PRIMARY_Y);
|
|
if (series_[i]) {
|
|
lv_chart_set_series_ext_y_array(chart_, series_[i], seriesData_[i].data());
|
|
std::fill(seriesData_[i].begin(), seriesData_[i].end(), HistoryStore::NO_POINT);
|
|
}
|
|
}
|
|
|
|
//applyAxisLabels();
|
|
//refreshData();
|
|
return obj_;
|
|
}
|
|
|
|
void ChartWidget::applyStyle() {
|
|
if (!obj_) return;
|
|
|
|
Widget::applyStyle();
|
|
lv_obj_set_style_border_width(obj_, 0, 0);
|
|
lv_obj_set_style_pad_all(obj_, 0, 0);
|
|
|
|
if (chart_) {
|
|
lv_obj_set_style_border_width(chart_, 0, 0);
|
|
lv_obj_set_style_pad_all(chart_, 6, 0);
|
|
lv_obj_set_style_line_width(chart_, 2, LV_PART_ITEMS);
|
|
}
|
|
|
|
const lv_color_t textColor = lv_color_make(
|
|
config_.textColor.r, config_.textColor.g, config_.textColor.b);
|
|
const lv_font_t* axisFont = Fonts::bySizeIndex(0);
|
|
if (yScale_) {
|
|
lv_obj_set_style_text_color(yScale_, textColor, LV_PART_INDICATOR);
|
|
lv_obj_set_style_text_font(yScale_, axisFont, LV_PART_INDICATOR);
|
|
lv_obj_set_style_bg_opa(yScale_, LV_OPA_TRANSP, 0);
|
|
lv_obj_set_style_border_width(yScale_, 0, 0);
|
|
}
|
|
if (xScale_) {
|
|
lv_obj_set_style_text_color(xScale_, textColor, LV_PART_INDICATOR);
|
|
lv_obj_set_style_text_font(xScale_, axisFont, LV_PART_INDICATOR);
|
|
lv_obj_set_style_bg_opa(xScale_, LV_OPA_TRANSP, 0);
|
|
lv_obj_set_style_border_width(xScale_, 0, 0);
|
|
}
|
|
}
|
|
|
|
void ChartWidget::onHistoryUpdate() {
|
|
refreshData();
|
|
}
|
|
|
|
void ChartWidget::refreshData() {
|
|
if (!chart_) return;
|
|
|
|
uint8_t count = config_.chartSeriesCount;
|
|
if (count > CHART_MAX_SERIES) count = CHART_MAX_SERIES;
|
|
|
|
bool hasAny = false;
|
|
int32_t globalMin = 0;
|
|
int32_t globalMax = 0;
|
|
|
|
for (uint8_t i = 0; i < count; ++i) {
|
|
if (!series_[i]) continue;
|
|
|
|
HistoryStore::instance().fillChartSeries(
|
|
config_.chartKnxAddress[i],
|
|
config_.chartTextSource[i],
|
|
static_cast<ChartPeriod>(config_.chartPeriod),
|
|
seriesData_[i].data(),
|
|
seriesData_[i].size());
|
|
|
|
for (size_t j = 0; j < seriesData_[i].size(); ++j) {
|
|
int32_t value = seriesData_[i][j];
|
|
if (value == HistoryStore::NO_POINT) continue;
|
|
if (!hasAny) {
|
|
globalMin = value;
|
|
globalMax = value;
|
|
hasAny = true;
|
|
} else {
|
|
globalMin = std::min(globalMin, value);
|
|
globalMax = std::max(globalMax, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hasAny) {
|
|
int32_t range = globalMax - globalMin;
|
|
if (range < 1) range = 1;
|
|
int32_t pad = range / 10;
|
|
if (pad < 1) pad = 1;
|
|
int32_t minVal = globalMin - pad;
|
|
int32_t maxVal = globalMax + pad;
|
|
lv_chart_set_axis_range(chart_, LV_CHART_AXIS_PRIMARY_Y, minVal, maxVal);
|
|
if (yScale_) {
|
|
lv_scale_set_range(yScale_, minVal, maxVal);
|
|
}
|
|
} else {
|
|
lv_chart_set_axis_range(chart_, LV_CHART_AXIS_PRIMARY_Y, 0, 100);
|
|
if (yScale_) {
|
|
lv_scale_set_range(yScale_, 0, 100);
|
|
}
|
|
}
|
|
|
|
lv_chart_refresh(chart_);
|
|
}
|
|
|
|
const char** ChartWidget::labelsForPeriod(ChartPeriod period, uint8_t* count) {
|
|
static const char* k1h[] = { "-60m", "-45m", "-30m", "-15m", "0", nullptr };
|
|
static const char* k3h[] = { "-3h", "-2h", "-1h", "-30m", "0", nullptr };
|
|
static const char* k5h[] = { "-5h", "-4h", "-3h", "-2h", "-1h", "0", nullptr };
|
|
static const char* k12h[] = { "-12h", "-9h", "-6h", "-3h", "0", nullptr };
|
|
static const char* k24h[] = { "-24h", "-18h", "-12h", "-6h", "0", nullptr };
|
|
static const char* k1m[] = { "-30d", "-21d", "-14d", "-7d", "0", nullptr };
|
|
|
|
switch (period) {
|
|
case ChartPeriod::HOUR_1:
|
|
if (count) *count = 5;
|
|
return k1h;
|
|
case ChartPeriod::HOUR_3:
|
|
if (count) *count = 5;
|
|
return k3h;
|
|
case ChartPeriod::HOUR_5:
|
|
if (count) *count = 6;
|
|
return k5h;
|
|
case ChartPeriod::HOUR_12:
|
|
if (count) *count = 5;
|
|
return k12h;
|
|
case ChartPeriod::HOUR_24:
|
|
if (count) *count = 5;
|
|
return k24h;
|
|
case ChartPeriod::MONTH_1:
|
|
if (count) *count = 5;
|
|
return k1m;
|
|
default:
|
|
if (count) *count = 5;
|
|
return k1h;
|
|
}
|
|
}
|
|
|
|
void ChartWidget::applyAxisLabels() {
|
|
if (!xScale_) return;
|
|
uint8_t count = 5;
|
|
const char** labels = labelsForPeriod(static_cast<ChartPeriod>(config_.chartPeriod), &count);
|
|
lv_scale_set_total_tick_count(xScale_, count);
|
|
lv_scale_set_major_tick_every(xScale_, 1);
|
|
lv_scale_set_text_src(xScale_, labels);
|
|
}
|