229 lines
7.8 KiB
C
229 lines
7.8 KiB
C
/*
|
|
* statsby.c - Screen for view stats by a specified period (day, week, etc.)
|
|
* Copyright (C) 2024 Alexander Rosenberg
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License as published by the Free Software
|
|
* Foundation, either version 3 of the License, or (at your option) any later
|
|
* version. See the LICENSE file for more information.
|
|
*/
|
|
#include "statsby.h"
|
|
|
|
#include <err.h>
|
|
#include <string.h>
|
|
|
|
static void stats_by_select_period(StatsByScreen *screen, SensorState *state) {
|
|
if (state->up_down) {
|
|
screen->period = (screen->period + 1) % NPERIOD;
|
|
screen->need_redraw = true;
|
|
}
|
|
if (state->down_down) {
|
|
if (--screen->period < 0) {
|
|
screen->period = NPERIOD - 1;
|
|
}
|
|
screen->need_redraw = true;
|
|
}
|
|
if (screen->need_redraw) {
|
|
lcd_clear(state->lcd);
|
|
lcd_move_to(state->lcd, 0, 0);
|
|
lcd_write_string(state->lcd, "Period:");
|
|
lcd_move_to(state->lcd, 1, 0);
|
|
lcd_write_string(state->lcd, ">");
|
|
lcd_write_string(state->lcd, PERIOD_LABELS[screen->period]);
|
|
lcd_move_to(state->lcd, 1, 0);
|
|
lcd_display_control(state->lcd, LCD_CURSOR_BLINK, LCD_CURSOR_ON,
|
|
LCD_DISPLAY_ON);
|
|
screen->need_redraw = false;
|
|
}
|
|
if (state->sel_down) {
|
|
++screen->stage;
|
|
date_sel_set_period(&screen->ds, screen->period);
|
|
screen->need_redraw = true;
|
|
}
|
|
}
|
|
|
|
static void stats_by_select_start(StatsByScreen *screen, SensorState *state) {
|
|
if (state->up_down || state->down_down || state->sel_down || state->back_down) {
|
|
screen->need_redraw = true;
|
|
}
|
|
if (screen->need_redraw) {
|
|
screen->need_redraw = false;
|
|
DateSelectionState dss = date_sel_dispatch(&screen->ds, state, "Start:");
|
|
switch (dss) {
|
|
case DATE_SEL_CONTINUE:
|
|
break; // ignore
|
|
case DATE_SEL_DONE:
|
|
++screen->stage;
|
|
screen->offset_scale = 0;
|
|
screen->need_redraw = true;
|
|
screen->blimit_reached = false;
|
|
screen->ulimit_reached = false;
|
|
break;
|
|
case DATE_SEL_BACK:
|
|
case DATE_SEL_ERROR:
|
|
--screen->stage;
|
|
screen->need_redraw = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void stats_by_draw_current_view (StatsByScreen *screen,
|
|
SensorState *state,
|
|
UtilAveragePeriod *data) {
|
|
char desc_string[17];
|
|
char data_string[17];
|
|
char humid_str[6];
|
|
switch (screen->view) {
|
|
case STATS_BY_AVG:
|
|
// switch-ception
|
|
switch (screen->period) {
|
|
case PERIOD_YEAR:
|
|
snprintf(desc_string, 17, "Year>%d", data->year);
|
|
break;
|
|
case PERIOD_MONTH:
|
|
snprintf(desc_string, 17, "Month>%d-%d", data->year,
|
|
data->month);
|
|
break;
|
|
case PERIOD_WEEK:
|
|
snprintf(desc_string, 17, "Week>%d-%d-%d", data->year,
|
|
data->month, data->day);
|
|
break;
|
|
case PERIOD_DAY:
|
|
snprintf(desc_string, 17, "Day>%d-%d-%d", data->year,
|
|
data->month, data->day);
|
|
break;
|
|
case PERIOD_HOUR:
|
|
snprintf(desc_string, 17, "Hour>%d-%d %d:00", data->month,
|
|
data->day, data->hour);
|
|
break;
|
|
}
|
|
if (data->npoints) { // prevent UB
|
|
snprintf(data_string, 17, "T:%.1f%c H:%d%%",
|
|
convert_temperature(data->avgtemp), GLOBAL_OPTS.temp_unit,
|
|
data->avghumid);
|
|
}
|
|
break;
|
|
case STATS_BY_TEMP:
|
|
strcpy(desc_string, "Max Min");
|
|
if (data->npoints) { // prevent UB
|
|
snprintf(data_string, 17, "%-4.1f%c %.1f%c",
|
|
convert_temperature(data->maxtemp), GLOBAL_OPTS.temp_unit,
|
|
convert_temperature(data->mintemp), GLOBAL_OPTS.temp_unit);
|
|
}
|
|
break;
|
|
case STATS_BY_HUMID:
|
|
if (data->npoints) { // prevent UB
|
|
strcpy(desc_string, "Max Min");
|
|
snprintf(data_string, 17, "%s %d%%",
|
|
pad_humid_str(data->maxhumid, humid_str, 6),
|
|
data->minhumid);
|
|
}
|
|
break;
|
|
default:
|
|
warnx("attempt to draw bad stats-by view");
|
|
--screen->stage;
|
|
screen->need_redraw = true;
|
|
break;
|
|
}
|
|
lcd_move_to(state->lcd, 0, 0);
|
|
lcd_write_string(state->lcd, desc_string);
|
|
lcd_move_to(state->lcd, 1, 0);
|
|
if (data->npoints) {
|
|
lcd_write_string(state->lcd, data_string);
|
|
} else {
|
|
lcd_write_string(state->lcd, "No data!");
|
|
}
|
|
}
|
|
|
|
static void stats_by_show_stats(StatsByScreen *screen, SensorState *state) {
|
|
if (state->back_down) {
|
|
screen->stage = STATS_BY_SELECT_PERIOD;
|
|
screen->need_redraw = true;
|
|
screen->ds.stage = DATE_SEL_YEAR;
|
|
screen->view = STATS_BY_AVG;
|
|
return;
|
|
}
|
|
if (state->sel_down) {
|
|
screen->view = (screen->view + 1) % STATS_BY_NVIEW;
|
|
screen->need_redraw = true;
|
|
}
|
|
if (screen->need_redraw) {
|
|
screen->need_redraw = false;
|
|
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
|
LCD_DISPLAY_ON);
|
|
lcd_clear(state->lcd);
|
|
if (screen->period == PERIOD_HOUR &&
|
|
screen->offset_scale == 0 &&
|
|
screen->ds.year == screen->ds.start_time.local_year &&
|
|
screen->ds.month == screen->ds.start_time.local_month &&
|
|
screen->ds.day == screen->ds.start_time.local_day) {
|
|
screen->offset_scale = screen->ds.start_time.local_hour;
|
|
}
|
|
UtilAveragePeriod data;
|
|
if (!get_average_for_period(state->db, screen->ds.year, screen->ds.month,
|
|
screen->ds.day, screen->offset_scale,
|
|
screen->period, &data)) {
|
|
lcd_write_string(state->lcd, "Query failed!");
|
|
warnx("failed to query average temperature and humidity");
|
|
return;
|
|
}
|
|
screen->ulimit_reached = data.upper_bound;
|
|
screen->blimit_reached = data.lower_bound;
|
|
stats_by_draw_current_view(screen, state, &data);
|
|
}
|
|
if (!screen->ulimit_reached && state->up_down) {
|
|
++screen->offset_scale;
|
|
screen->need_redraw = true;
|
|
}
|
|
if (!screen->blimit_reached && state->down_down) {
|
|
--screen->offset_scale;
|
|
screen->need_redraw = true;
|
|
}
|
|
}
|
|
|
|
static bool stats_by_screen_dispatch(StatsByScreen *screen,
|
|
SensorState *state) {
|
|
if (state->force_draw) {
|
|
screen->need_redraw = true;
|
|
}
|
|
if (state->back_down) {
|
|
if (screen->stage == STATS_BY_SELECT_PERIOD) {
|
|
screen->period = PERIOD_HOUR;
|
|
screen->need_redraw = true;
|
|
lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF,
|
|
LCD_DISPLAY_ON);
|
|
date_sel_reset(&screen->ds);
|
|
return true;
|
|
}
|
|
}
|
|
switch (screen->stage) {
|
|
case STATS_BY_SELECT_PERIOD:
|
|
stats_by_select_period(screen, state);
|
|
break;
|
|
case STATS_BY_SELECT_START:
|
|
stats_by_select_start(screen, state);
|
|
break;
|
|
case STATS_BY_SHOWING:
|
|
stats_by_show_stats(screen, state);
|
|
break;
|
|
default:
|
|
LOG_VERBOSE("Attempt to show bad stats by screen\n");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
StatsByScreen *stats_by_screen_new() {
|
|
StatsByScreen *s = malloc_checked(sizeof(StatsByScreen));
|
|
screen_init(&s->parent, "Stats by",
|
|
(ScreenDispatchFunc) stats_by_screen_dispatch,
|
|
(ScreenCleanupFunc) free);
|
|
s->need_redraw = true;
|
|
s->stage = STATS_BY_SELECT_PERIOD;
|
|
s->period = PERIOD_HOUR;
|
|
s->view = STATS_BY_AVG;
|
|
date_sel_init(&s->ds, PERIOD_DAY);
|
|
return s;
|
|
}
|