/* * util.h - Utility functions * 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. */ #ifndef INCLUDED_UTIL_H #define INCLUDED_UTIL_H #include #include #include #include #include #include #include #include #define _STRINGIFY_LIT(s) (#s) #define STRINGIFY(v) _STRINGIFY_LIT(v) typedef enum { TEMP_UNIT_F = 'F', TEMP_UNIT_C = 'C' } TemperatureUnit; typedef struct { char *config_path; // path to config file bool strict_config; // exit if unknown config options is found bool verbose; // be more verbose char *gpio_path; // path of GPIO device file to use gpio_pin_t en_pin; gpio_pin_t rs_pin; gpio_pin_t rw_pin; gpio_pin_t data_pins[8]; gpio_pin_t bl_pin; char *database_location; // location of sqlite3 database char *temp_key; // sysctl key for temperature char *humid_key; // sysctl key for humidity char *fail_key; // sysctl key for number of fails uint32_t fail_limit; // limit for number of failures before exit uint32_t refresh_time; // time between temp and humid refresh // menu navigation pins gpio_pin_t up_pin; gpio_pin_t down_pin; gpio_pin_t back_pin; gpio_pin_t sel_pin; TemperatureUnit temp_unit; char *export_file_name; // file to export to } Options; extern Options GLOBAL_OPTS; /* * Cleanup an Options struct. */ void cleanup_options(Options *opts); /* * Like malloc(3), except that if allocation fails, an error will be written to * standard error and abort(3) called */ void *malloc_checked(size_t n); /* * Like realloc(3), except that if allocation fails, an error will be written to * standard error and abort(3) called */ void *realloc_checked(void *old_ptr, size_t n); /* * Like strdup(3), except that if allocation fails, an error will be written to * standard error and abort(3) called */ void *strdup_checked(const char *str); /* * Like asprintf(3), except that if allocation fails, an error will be written to * standard error and abort(3) called */ int asprintf_checked(char **str, const char *format, ...) __attribute__((format(printf, 2, 3))); /* * Call fprintf(3) to stderr only if verbose mode was enabled. */ #define LOG_VERBOSE(...) if (GLOBAL_OPTS.verbose) {fprintf(stderr, __VA_ARGS__);} /* * Return: the number of days in month M. 1 is January. Y is the year. */ int days_in_month(int m, int y); /* * Initialize SQL queries used by this file. */ void initialize_util_queries(sqlite3 *db); /* * Cleanup SQL queries used by this file. */ void cleanup_util_queries(void); typedef struct { int64_t utc; int utc_year; int utc_month; int utc_day; int utc_hour; int utc_minute; int utc_second; int64_t local; int local_year; int local_month; int local_day; int local_hour; int local_minute; int local_second; } UtilDate; enum { PERIOD_HOUR = 0, PERIOD_DAY, PERIOD_WEEK, PERIOD_MONTH, PERIOD_YEAR, }; typedef int UtilPeriod; extern const char *PERIOD_LABELS[]; extern const size_t NPERIOD; /* * Return the START of the first and END of the last PERIOD (ex. week) of DB. * Also get the hour, minute, and second limits for the first and last day. * Return: false if an error occurred, true otherwise. */ bool get_database_limits(sqlite3 *db, UtilPeriod period, UtilDate *start, UtilDate *end); typedef struct { int64_t npoints; int avgtemp; int avghumid; int maxtemp; int maxhumid; int mintemp; int minhumid; int year; int month; int day; int hour; bool lower_bound; bool upper_bound; } UtilAveragePeriod; /* * Get the average temp. and humid. between YEAR-MONTH-DAY + COUNT * PERIOD and * YEAR-MONTH-DAY + (COUNT + 1) * PERIOD. * 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); typedef struct { int64_t time; int temp; int humid; bool has_next; int64_t next_time; int next_temp; int next_humid; bool has_prev; int64_t prev_time; int prev_temp; int prev_humid; } UtilDataPointInfo; /* * Get the data, next point, and previous point for data point TIME. INFO must * not be NULL. * Return: false if an error occurred, true otherwise. */ bool get_data_point_info(sqlite3 *db, int64_t time, UtilDataPointInfo *info); typedef struct { int64_t npoints; int avgtemp; int avghumid; int maxtemp; int mintemp; int maxhumid; int minhumid; } UtilAverageRange; /* * Get the average DATA for the range of UTC times START to END (inclusive). * Return: false if an error occurred, true otherwise. */ bool get_average_for_range(sqlite3 *db, uint64_t start, uint64_t end, UtilAverageRange *data); /* * Convert the temperature in deciKelvin DK to either F or C. */ 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); /* * Like recursive_rm (see below), except that PATH is resolved relative to DFD. */ bool recursive_rm_at(int dfd, const char *path); /* * Call recursive_rm on all of FILE's children, the remove FILE * Return: false on error, true otherwise */ bool recursive_rm(const char *path); /* * Use the sqlite3 backup API to export DB to dest. This function is safe to * call in a multi-threaded environment where pthread_cancel may be used. * Return: true on success, false on error */ bool export_database(sqlite3 *db, const char *dest); /* * Export DB as a CSV file to DEST. This function is safe to call in a * multi-threaded environment where pthread_cancel may be used. * Return: true on success, false on error */ bool export_database_as_csv(sqlite3 *db, const char *dest); /* * Create all the missing directories along path. * Return: true on success, false otherwise */ bool mkdirs(const char *path, mode_t mode); #endif