Add min and max temp and humid screen
This commit is contained in:
		| @ -148,7 +148,7 @@ static bool stats_screen_dispatch(StatsScreen *screen, SensorState *state) { | ||||
|         lcd_clear(state->lcd); | ||||
|         lcd_write_string(state->lcd, "temp  humi time"); | ||||
|         char buff[17]; | ||||
|         int cur_len = snprintf(buff, sizeof(buff), "%.1f%c %3" PRIu32 "%% ", | ||||
|         int cur_len = snprintf(buff, sizeof(buff), "%-4.1f%c %3" PRIu32 "%% ", | ||||
|                                convert_temperature(state->temp), | ||||
|                                GLOBAL_OPTS.temp_unit, state->humid); | ||||
|         struct tm lt; | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
|  */ | ||||
| #include "statrange.h" | ||||
|  | ||||
| #include <string.h> | ||||
| #include <inttypes.h> | ||||
| #include <time.h> | ||||
| #include <err.h> | ||||
| @ -92,6 +93,10 @@ static void stat_range_show_data(StatRangeScreen *screen, SensorState *state) { | ||||
|         screen->stage = STAT_RANGE_START_DATE; | ||||
|         return; | ||||
|     } | ||||
|     if (state->sel_down) { | ||||
|         screen->need_redraw = true; | ||||
|         screen->view = (screen->view + 1) % STAT_RANGE_NVIEW; | ||||
|     } | ||||
|     if (screen->need_redraw) { | ||||
|         screen->need_redraw = false; | ||||
|         uint64_t start = to_uts_timestamp(&screen->sds, &screen->sts); | ||||
| @ -108,17 +113,45 @@ static void stat_range_show_data(StatRangeScreen *screen, SensorState *state) { | ||||
|             --screen->stage; | ||||
|             return; | ||||
|         } | ||||
|         char desc_line[17]; | ||||
|         char data_line[17]; | ||||
|         char humid_str[6]; | ||||
|         switch (screen->view) { | ||||
|         case STAT_RANGE_AVG: | ||||
|             snprintf(desc_line, 17, "%" PRIi64 "pts:", data.npoints); | ||||
|             if (data.npoints) { // prevent UB | ||||
|                 snprintf(data_line, 17, "%.1f%c %d%%", | ||||
|                          convert_temperature(data.avgtemp), | ||||
|                          GLOBAL_OPTS.temp_unit, data.avghumid); | ||||
|             } | ||||
|             break;  | ||||
|         case STAT_RANGE_TEMP: | ||||
|             strcpy(desc_line, "Max   Min"); | ||||
|             if (data.npoints) { // prevent UB | ||||
|                 snprintf(data_line, 17, "%-4.1f%c %.1f%c", | ||||
|                          convert_temperature(data.maxtemp), GLOBAL_OPTS.temp_unit, | ||||
|                          convert_temperature(data.mintemp), GLOBAL_OPTS.temp_unit); | ||||
|             } | ||||
|             break; | ||||
|         case STAT_RANGE_HUMID: | ||||
|             strcpy(desc_line, "Max   Min"); | ||||
|             if (data.npoints) { // prevent UB | ||||
|                 snprintf(data_line, 17, "%s %d%%", | ||||
|                          pad_humid_str(data.maxhumid, humid_str, 6), | ||||
|                          data.minhumid); | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             warnx("attempt to draw bad stat range view"); | ||||
|             --screen->stage; | ||||
|             screen->need_redraw = true; | ||||
|             break; | ||||
|         } | ||||
|         lcd_clear(state->lcd); | ||||
|         lcd_move_to(state->lcd, 0, 0); | ||||
|         char points_line[17]; | ||||
|         snprintf(points_line, 17, "%" PRIi64 "pts:", data.npoints); | ||||
|         lcd_write_string(state->lcd, points_line); | ||||
|         lcd_write_string(state->lcd, desc_line); | ||||
|         lcd_move_to(state->lcd, 1, 0); | ||||
|         if (data.npoints) { | ||||
|             char data_line[17]; | ||||
|             snprintf(data_line, 17, "%.1f%c %d%%", | ||||
|                      convert_temperature(data.temp), | ||||
|                      GLOBAL_OPTS.temp_unit, data.humid); | ||||
|             lcd_write_string(state->lcd, data_line); | ||||
|         } else { | ||||
|             lcd_write_string(state->lcd, "No data!"); | ||||
| @ -134,6 +167,7 @@ static bool stat_range_screen_dispatch(StatRangeScreen *screen, | ||||
|     switch (screen->stage) { | ||||
|     case STAT_RANGE_START_DATE: | ||||
|         if(stat_range_handle_date(screen, &screen->sds, state, "Start Date:")) { | ||||
|             screen->view = STAT_RANGE_AVG; | ||||
|             date_sel_reset(&screen->sds); | ||||
|             time_sel_reset(&screen->sts); | ||||
|             date_sel_reset(&screen->eds); | ||||
| @ -166,6 +200,7 @@ StatRangeScreen *stat_range_screen_new(void) { | ||||
|                 (ScreenCleanupFunc) free); | ||||
|     s->need_redraw = true; | ||||
|     s->stage = STAT_RANGE_START_DATE; | ||||
|     s->view = STAT_RANGE_AVG; | ||||
|     date_sel_init(&s->sds, PERIOD_DAY); | ||||
|     time_sel_init(&s->sts, &s->sds, true); | ||||
|     date_sel_init(&s->eds, PERIOD_DAY); | ||||
|  | ||||
| @ -22,6 +22,12 @@ typedef struct { | ||||
|         STAT_RANGE_END_TIME, | ||||
|         STAT_RANGE_SHOW | ||||
|     } stage; | ||||
|     enum { | ||||
|         STAT_RANGE_AVG = 0, | ||||
|         STAT_RANGE_TEMP, | ||||
|         STAT_RANGE_HUMID, | ||||
|         STAT_RANGE_NVIEW | ||||
|     } view; | ||||
|     bool need_redraw; | ||||
|     DateSelection sds; | ||||
|     TimeSelection sts; | ||||
|  | ||||
							
								
								
									
										114
									
								
								src/ui/statsby.c
									
									
									
									
									
								
							
							
						
						
									
										114
									
								
								src/ui/statsby.c
									
									
									
									
									
								
							| @ -10,6 +10,7 @@ | ||||
| #include "statsby.h" | ||||
|  | ||||
| #include <err.h> | ||||
| #include <string.h> | ||||
|  | ||||
| static void stats_by_select_period(StatsByScreen *screen, SensorState *state) { | ||||
|     if (state->up_down) { | ||||
| @ -67,17 +68,91 @@ static void stats_by_select_start(StatsByScreen *screen, SensorState *state) { | ||||
|     }  | ||||
| } | ||||
|  | ||||
| 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; | ||||
|     } else if (screen->need_redraw) { | ||||
|         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); | ||||
|         lcd_move_to(state->lcd, 0, 0); | ||||
|         if (screen->period == PERIOD_HOUR &&  | ||||
|             screen->offset_scale == 0 && | ||||
|             screen->ds.year == screen->ds.start_time.local_year && | ||||
| @ -95,39 +170,7 @@ static void stats_by_show_stats(StatsByScreen *screen, SensorState *state) { | ||||
|         } | ||||
|         screen->ulimit_reached = data.upper_bound; | ||||
|         screen->blimit_reached = data.lower_bound; | ||||
|         char period_string[17]; | ||||
|         switch (screen->period) { | ||||
|         case PERIOD_YEAR: | ||||
|             snprintf(period_string, 17, "Year>%d", data.year); | ||||
|             break; | ||||
|         case PERIOD_MONTH: | ||||
|             snprintf(period_string, 17, "Month>%d-%d", data.year, | ||||
|                      data.month); | ||||
|             break; | ||||
|         case PERIOD_WEEK: | ||||
|             snprintf(period_string, 17, "Week>%d-%d-%d", data.year, | ||||
|                      data.month, data.day); | ||||
|             break; | ||||
|         case PERIOD_DAY: | ||||
|             snprintf(period_string, 17, "Day>%d-%d-%d", data.year, | ||||
|                      data.month, data.day); | ||||
|             break; | ||||
|         case PERIOD_HOUR: | ||||
|             snprintf(period_string, 17, "Hour>%d-%d %d:00", data.month, | ||||
|                      data.day, data.hour); | ||||
|             break; | ||||
|         } | ||||
|         lcd_write_string(state->lcd, period_string); | ||||
|         lcd_move_to(state->lcd, 1, 0); | ||||
|         if (data.npoints) { | ||||
|             char data_string[17]; | ||||
|             snprintf(data_string, 17, "T:%.1f%c H:%d%%", | ||||
|                      convert_temperature(data.temp), GLOBAL_OPTS.temp_unit, | ||||
|                      data.humid); | ||||
|             lcd_write_string(state->lcd, data_string); | ||||
|         } else { | ||||
|             lcd_write_string(state->lcd, "No data!"); | ||||
|         } | ||||
|         stats_by_draw_current_view(screen, state, &data); | ||||
|     } | ||||
|     if (!screen->ulimit_reached && state->up_down) { | ||||
|         ++screen->offset_scale; | ||||
| @ -178,6 +221,7 @@ StatsByScreen *stats_by_screen_new() { | ||||
|     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; | ||||
| } | ||||
|  | ||||
| @ -19,6 +19,12 @@ typedef struct { | ||||
|         STATS_BY_SELECT_START, | ||||
|         STATS_BY_SHOWING, | ||||
|     } stage; | ||||
|     enum { | ||||
|         STATS_BY_AVG = 0, | ||||
|         STATS_BY_TEMP, | ||||
|         STATS_BY_HUMID, | ||||
|         STATS_BY_NVIEW, | ||||
|     } view; | ||||
|     bool need_redraw; | ||||
|     UtilPeriod period; | ||||
|     DateSelection ds; | ||||
|  | ||||
							
								
								
									
										43
									
								
								src/util.c
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								src/util.c
									
									
									
									
									
								
							| @ -129,13 +129,21 @@ static const char *AVG_FOR_PERIOD_QUERY_STR = | ||||
|     "etime >= (select max(time) from env_data) as ulimit,\n" | ||||
|     "stime <= (select min(time) from env_data) as blimit,\n" | ||||
|     "atemp,\n" | ||||
|     "ahumid\n" | ||||
|     "ahumid,\n" | ||||
|     "maxtemp,\n" | ||||
|     "mintemp,\n" | ||||
|     "maxhumid,\n" | ||||
|     "minhumid\n" | ||||
|     "FROM (SELECT\n" | ||||
|     "unixepoch(printf('%04d-%02d-%02d', ?1, ?2, ?3), 'utc', printf('%d %s', ?4 * ?6, ?5)) as stime,\n" | ||||
|     "unixepoch(printf('%04d-%02d-%02d', ?1, ?2, ?3), 'utc', printf('%d %s', (?4 + 1) * ?6, ?5)) as etime,\n" | ||||
|     "count(time) AS ndata,\n" | ||||
|     "round(avg(temp)) AS atemp,\n" | ||||
|     "round(avg(humid)) AS ahumid\n" | ||||
|     "round(avg(humid)) AS ahumid,\n" | ||||
|     "max(temp) AS maxtemp,\n" | ||||
|     "min(temp) AS mintemp,\n" | ||||
|     "max(humid) AS maxhumid,\n" | ||||
|     "min(humid) AS minhumid\n" | ||||
|     "FROM env_data\n" | ||||
|     "WHERE time >= stime\n" | ||||
|     "AND time <= etime);\n"; | ||||
| @ -145,10 +153,11 @@ static const char *DATA_POINT_QUERY_STR = | ||||
|     "UNION ALL\n" | ||||
|     "SELECT min(time) IS NULL, min(time), temp, humid FROM env_data WHERE time > ?1\n" | ||||
|     "UNION ALL\n" | ||||
|     "SELECT false, * FROM env_data WHERE time == ?1;"; | ||||
|     "SELECT false, time, temp, humid FROM env_data WHERE time == ?1;"; | ||||
| static sqlite3_stmt *DATA_POINT_QUERY; | ||||
| static const char *AVG_FOR_RANGE_QUERY_STR = | ||||
|     "SELECT count(time), round(avg(temp)), round(avg(humid)) FROM env_data\n" | ||||
|     "SELECT count(time), round(avg(temp)), round(avg(humid)),\n" | ||||
|     "       max(temp), min(temp), max(humid), min(humid) FROM env_data\n" | ||||
|     "WHERE time >= ?1 AND time <= ?2;"; | ||||
| static sqlite3_stmt *AVG_FOR_RANGE_QUERY; | ||||
| void initialize_util_queries(sqlite3 *db) { | ||||
| @ -257,8 +266,12 @@ bool get_average_for_period(sqlite3 *db, int year, int month, int day, | ||||
|         data->npoints = sqlite3_column_int64(AVG_FOR_PERIOD_QUERY, 4); | ||||
|         data->upper_bound = sqlite3_column_int64(AVG_FOR_PERIOD_QUERY, 5); | ||||
|         data->lower_bound = sqlite3_column_int64(AVG_FOR_PERIOD_QUERY, 6); | ||||
|         data->temp = sqlite3_column_int(AVG_FOR_PERIOD_QUERY, 7); | ||||
|         data->humid = sqlite3_column_int(AVG_FOR_PERIOD_QUERY, 8); | ||||
|         data->avgtemp = sqlite3_column_int(AVG_FOR_PERIOD_QUERY, 7); | ||||
|         data->avghumid = sqlite3_column_int(AVG_FOR_PERIOD_QUERY, 8); | ||||
|         data->maxtemp = sqlite3_column_int(AVG_FOR_PERIOD_QUERY, 9); | ||||
|         data->mintemp = sqlite3_column_int(AVG_FOR_PERIOD_QUERY, 10); | ||||
|         data->maxhumid = sqlite3_column_int(AVG_FOR_PERIOD_QUERY, 11); | ||||
|         data->minhumid = sqlite3_column_int(AVG_FOR_PERIOD_QUERY, 12); | ||||
|     } | ||||
|     sqlite3_reset(AVG_FOR_PERIOD_QUERY); | ||||
|     return success; | ||||
| @ -325,8 +338,12 @@ bool get_average_for_range(sqlite3 *db, uint64_t start, uint64_t end, | ||||
|         success = false; | ||||
|     } else if (data) { | ||||
|         data->npoints = sqlite3_column_int64(AVG_FOR_RANGE_QUERY, 0); | ||||
|         data->temp = sqlite3_column_int(AVG_FOR_RANGE_QUERY, 1); | ||||
|         data->humid = sqlite3_column_int(AVG_FOR_RANGE_QUERY, 2); | ||||
|         data->avgtemp = sqlite3_column_int(AVG_FOR_RANGE_QUERY, 1); | ||||
|         data->avghumid = sqlite3_column_int(AVG_FOR_RANGE_QUERY, 2); | ||||
|         data->maxtemp = sqlite3_column_int(AVG_FOR_RANGE_QUERY, 3); | ||||
|         data->mintemp = sqlite3_column_int(AVG_FOR_RANGE_QUERY, 4); | ||||
|         data->maxhumid = sqlite3_column_int(AVG_FOR_RANGE_QUERY, 5); | ||||
|         data->minhumid = sqlite3_column_int(AVG_FOR_RANGE_QUERY, 6); | ||||
|     } | ||||
|     sqlite3_reset(AVG_FOR_RANGE_QUERY); | ||||
|     return success; | ||||
| @ -352,3 +369,13 @@ float convert_temperature(int dk) { | ||||
|         return 0.0f; | ||||
|     } | ||||
| } | ||||
|  | ||||
| char *pad_humid_str(int humid, char *buf, size_t buf_size) { | ||||
|     int fmt_len = snprintf(buf, buf_size, "%d%%", humid); | ||||
|     ssize_t remaining = buf_size - fmt_len - 1; | ||||
|     if (remaining > 0) { | ||||
|         memset(buf + fmt_len, ' ', remaining); | ||||
|         buf[buf_size - 1] = '\0'; | ||||
|     } | ||||
|     return buf; | ||||
| } | ||||
|  | ||||
							
								
								
									
										26
									
								
								src/util.h
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								src/util.h
									
									
									
									
									
								
							| @ -138,8 +138,12 @@ bool get_database_limits(sqlite3 *db, UtilPeriod period, UtilDate *start, | ||||
|  | ||||
| typedef struct { | ||||
|     int64_t npoints; | ||||
|     int temp; | ||||
|     int humid; | ||||
|     int avgtemp; | ||||
|     int avghumid; | ||||
|     int maxtemp; | ||||
|     int maxhumid; | ||||
|     int mintemp; | ||||
|     int minhumid; | ||||
|     int year; | ||||
|     int month; | ||||
|     int day; | ||||
| @ -154,8 +158,8 @@ typedef struct { | ||||
|  * Return: false if an error occurred, true otherwise. | ||||
|  */ | ||||
| bool get_average_for_period(sqlite3 *db, int year, int month, int day, | ||||
|                            int64_t count, UtilPeriod period, | ||||
|                            UtilAveragePeriod *data); | ||||
|                             int64_t count, UtilPeriod period, | ||||
|                             UtilAveragePeriod *data); | ||||
|  | ||||
| typedef struct { | ||||
|     int64_t time; | ||||
| @ -180,8 +184,12 @@ bool get_data_point_info(sqlite3 *db, int64_t time, UtilDataPointInfo *info); | ||||
|  | ||||
| typedef struct { | ||||
|     int64_t npoints; | ||||
|     int temp; | ||||
|     int humid; | ||||
|     int avgtemp; | ||||
|     int avghumid; | ||||
|     int maxtemp; | ||||
|     int mintemp; | ||||
|     int maxhumid; | ||||
|     int minhumid; | ||||
| } UtilAverageRange; | ||||
|  | ||||
| /* | ||||
| @ -196,4 +204,10 @@ bool get_average_for_range(sqlite3 *db, uint64_t start, uint64_t end, | ||||
|  */ | ||||
| float convert_temperature(int dk); | ||||
|  | ||||
| /* | ||||
|  * Fill BUF with at most BUF_SIZE characters (including the null byte), by | ||||
|  * padding "HUMID%" to BUF_SIZE - 1 characters. | ||||
|  */ | ||||
| char *pad_humid_str(int humid, char *buf, size_t buf_size); | ||||
|  | ||||
| #endif | ||||
|  | ||||
		Reference in New Issue
	
	Block a user