From 6e2c74a01ee884dd17cd7dda6c1c73f4e2a8556f Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Mon, 11 Mar 2024 16:30:14 +0100 Subject: [PATCH] Add support to decode config from Scialys module and signal handler to close (and flush) files when killed/exited --- host/scialys_decode/config.h | 77 +++++++++++++++++++++++++ host/scialys_decode/main.c | 109 +++++++++++++++++++++++++++++++++-- 2 files changed, 182 insertions(+), 4 deletions(-) create mode 100644 host/scialys_decode/config.h diff --git a/host/scialys_decode/config.h b/host/scialys_decode/config.h new file mode 100644 index 0000000..cf356c8 --- /dev/null +++ b/host/scialys_decode/config.h @@ -0,0 +1,77 @@ +/***************************************************************************** */ +/* Configuration storage */ + +/* This struct is stored to user flash when user validates new configuration + * and read from user flash upon statup + * Default values are defined within config.c + * Note that temperature values are stored as hundredth of degrees centigrade (1/100) + * + * Meaning of fields : + * + * grid_power_limit : + * Maximum power which can be used from the external (paid) power source, specified (in kWatt) + * source_power_max : + * Maximum power which can be produced by installation (in Watts) + * source_has_power_ok : + * Power production value to be considered as enough to trigger delayed force heating (in Watts) + * load_power_max : + * Maximum power used by the load. Currently unused by code (in Watts) + * conf_max_temp : + * Maximum temperature at which heating stops, whatever the condition (production or manual + * force included) [20 .. 90] + * + * enter_forced_mode_temp : + * temperature bellow which the system enters auto forced heating mode + * degrees centigrade [0 .. 50] (best not to set above 40) + * auto_forced_mode_value : + * command value to be used when in auto forced mode [10 .. 100] + * auto_forced_target_heater_temp : + * temperature up to which the water is heated when in auto forced mode [20 .. 90] + * auto_forced_heater_delay : + * time to wait before starting auto forced heating, in order to wait for production to start + * (sunrise, wind, ...) + * auto_forced_heater_duration : + * duration (hours) of auto forced heating [1 .. 10] + * auto_force_type : + * type of termination condition for manual force mode (off (no auto force) temp, time, both (first)) + * manual_forced_mode_value : + * command value to be used when in manual forced mode [10 .. 100] + * manual_target_heater_temp : + * temperature up to which the water is heated when in manual forced mode [20 .. 90] + * manual_activation_duration : + * duration (hours) of manual forced heating [1 .. 10] + * manual_force_type : + * type of termination condition for manual force mode (temp, time, both (first)) + * never_force : + * + */ +#define CONFIG_OK 0x4F4B /* "OK" in ASCII */ +#define CONFIG_VERSION 0x02 +struct scialys_config { + /* Config version and info */ + uint8_t conf_version; + uint8_t checksum; + uint16_t config_ok; + /* Config limits */ + uint16_t grid_power_limit; /* specified in kWatt */ + uint16_t source_power_max; /* specified in Watt */ + uint16_t source_has_power_ok; /* specified in Watt */ + uint16_t load_power_max; /* specified in Watt */ + uint16_t conf_max_temp; /* degrees centigrade (0 - 90°C) */ + /* Auto Force limits */ + uint16_t enter_forced_mode_temp; /* degrees centigrade (2 - 60°C) */ + uint16_t auto_forced_mode_value; /* % : 25 - 100 */ + uint16_t auto_forced_target_heater_temp; /* degrees centigrade (0 - 90°C) */ + uint16_t auto_forced_heater_delay; /* Hours : 0 - 5 */ + uint16_t auto_forced_heater_duration; /* Hours : 1 - 5 */ + uint16_t auto_force_type; /* Off, Min, Max, Timer, Target */ + /* Manual Force limits */ + uint16_t manual_forced_mode_value; /* % : 25 - 100 */ + uint16_t manual_target_heater_temp; /* degrees centigrade (0 - 90°C) */ + uint16_t manual_activation_duration; + uint16_t manual_force_type; /* Off, Min, Max, Timer, Target */ + /* Other */ + uint16_t load_type; /* LOAD_TYPE_AC_RES, LOAD_TYPE_AC_IND or LOAD_TYPE_DC */ + uint16_t never_force; /* 0 or 1 */ +} __attribute__((packed)); + diff --git a/host/scialys_decode/main.c b/host/scialys_decode/main.c index 2b98901..ab678d1 100644 --- a/host/scialys_decode/main.c +++ b/host/scialys_decode/main.c @@ -39,11 +39,14 @@ #include #include #include +#include #include #include #include +#include #include #include "serial_utils.h" +#include "config.h" #define PROG_NAME "Sensors polling and decode" @@ -67,18 +70,31 @@ void help(char *prog_name) #define BUF_SIZE 100 #define MSG_LEN 150 char data[MSG_LEN]; -int data_idx = 0; +uint8_t data_idx = 0; +char conf[MSG_LEN]; +uint8_t conf_idx = 0; int protocol_decode(char c) { - if (data_idx == 0) { + if ((data_idx == 0) && (conf_idx == 0)) { if (c == '#') { data[0] = c; data_idx = 1; + } else if (c == 'C') { + conf[0] = c; + conf_idx = 1; } return 0; } + if (conf_idx > 0) { + conf[conf_idx] = c; + conf_idx++; + if (conf_idx > (sizeof(struct scialys_config) + 1)) { + conf_idx = 0; + return 2; + } + } if (data_idx > 0) { data[data_idx] = c; if (c == '$') { @@ -94,6 +110,30 @@ int protocol_decode(char c) return 0; } +volatile int exit_loop = 0; +void catcher(int signum) +{ + printf("Caught signal %d, terminating\n", signum); + exit_loop = 1; +} +void install_handlers(void) +{ + struct sigaction setup_action; + sigset_t block_mask; + + sigemptyset(&block_mask); + /* Block other terminal-generated signals while handler runs. */ + sigaddset(&block_mask, SIGINT); + sigaddset(&block_mask, SIGQUIT); + sigaddset(&block_mask, SIGTERM); + setup_action.sa_handler = catcher; + setup_action.sa_mask = block_mask; + setup_action.sa_flags = 0; + sigaction(SIGQUIT, &setup_action, NULL); + sigaction(SIGTERM, &setup_action, NULL); + sigaction(SIGINT, &setup_action, NULL); +} + int main(int argc, char* argv[]) { /* Serial */ @@ -104,6 +144,7 @@ int main(int argc, char* argv[]) char* log_base_name = NULL; int data_log_fd = 0; int info_log_fd = 0; + FILE* conf_log_file = NULL; /* Timestamp */ struct timeval now; @@ -166,6 +207,9 @@ int main(int argc, char* argv[]) return -2; } + /* Install signal handlers */ + install_handlers(); + /* Log files ? */ if (log_base_name != NULL) { char name[MAX_LOGNAME_LEN]; @@ -183,6 +227,11 @@ int main(int argc, char* argv[]) if (info_log_fd <= 0) { printf("Unable to open Info log file '%s' : %s", name, strerror(errno)); } + snprintf(name, MAX_LOGNAME_LEN, "%s.config", log_base_name); + conf_log_file = fopen(name, "a"); + if (conf_log_file == NULL) { + printf("Unable to open Config log file '%s' : %s", name, strerror(errno)); + } } /* Save start second */ @@ -208,6 +257,9 @@ int main(int argc, char* argv[]) /* Read returnd 0 ... EOF */ printf("Serial disapeared ... closing and re-opening\n"); } + if (exit_loop == 1) { + break; + } close(slave_fd); do { slave_fd = serial_setup(device); @@ -230,8 +282,8 @@ int main(int argc, char* argv[]) /* Extract frames from data stream */ ret = protocol_decode(buf[idx]); /* Log ? */ - if ((info_log_fd > 0) && (ret == 0) && (data_idx == 0)) { - static char last_logged_char = '\0'; + if ((info_log_fd > 0) && (ret == 0) && (data_idx == 0) && (conf_idx == 0)) { + static char last_logged_char = '\n'; /* Add timestamps */ if ((last_logged_char == '\n') && (buf[idx] != '\n')) { char ts[20]; @@ -290,13 +342,62 @@ int main(int argc, char* argv[]) write(data_log_fd, data, strnlen(data, MSG_LEN)); write(data_log_fd, "\n", 1); } + } else if (ret == 2) { + struct scialys_config sc_conf; + uint8_t cksum = 0, i = 0; + memcpy(&sc_conf, (conf + 1), sizeof(struct scialys_config)); + /* Check config checksum */ + for (i = 0; i < sizeof(struct scialys_config); i++) { + cksum += conf[i + 1]; + } + if (cksum != 0) { + printf("Received possible config with bad checksum\n"); + } else if (conf_log_file != NULL) { + char confok[3] = {0}; + sc_conf.config_ok = bswap_16(sc_conf.config_ok); /* "OK" stored in little endian */ + memcpy(confok, &sc_conf.config_ok, 2); + fprintf(conf_log_file, "% 5d.%03d : Dumped internal configuration :\n", sec, msec); + fprintf(conf_log_file, " conf_version : %d\n", sc_conf.conf_version); + fprintf(conf_log_file, " config_ok : %s\n", confok); + fprintf(conf_log_file, " grid_power_limit : %d\n", sc_conf.grid_power_limit); + fprintf(conf_log_file, " source_power_max : %d\n", sc_conf.source_power_max); + fprintf(conf_log_file, " source_has_power_ok : %d\n", sc_conf.source_has_power_ok); + fprintf(conf_log_file, " load_power_max : %d\n", sc_conf.load_power_max); + fprintf(conf_log_file, " conf_max_temp : %d\n", sc_conf.conf_max_temp); + fprintf(conf_log_file, " enter_forced_mode_temp : %d\n", sc_conf.enter_forced_mode_temp); + fprintf(conf_log_file, " auto_forced_mode_value : %d\n", sc_conf.auto_forced_mode_value); + fprintf(conf_log_file, " auto_forced_target_heater_temp : %d\n", sc_conf.auto_forced_target_heater_temp); + fprintf(conf_log_file, " auto_forced_heater_delay : %d\n", sc_conf.auto_forced_heater_delay); + fprintf(conf_log_file, " auto_forced_heater_duration : %d\n", sc_conf.auto_forced_heater_duration); + fprintf(conf_log_file, " auto_force_type : %d\n", sc_conf.auto_force_type); + fprintf(conf_log_file, " manual_forced_mode_value : %d\n", sc_conf.manual_forced_mode_value); + fprintf(conf_log_file, " manual_target_heater_temp : %d\n", sc_conf.manual_target_heater_temp); + fprintf(conf_log_file, " manual_activation_duration : %d\n", sc_conf.manual_activation_duration); + fprintf(conf_log_file, " manual_force_type : %d\n", sc_conf.manual_force_type); + fprintf(conf_log_file, " load_type : %d\n", sc_conf.load_type); + fprintf(conf_log_file, " never_force : %d\n", sc_conf.never_force); + fsync(fileno(conf_log_file)); + } } idx++; } + if (exit_loop == 1) { + break; + } } /* End of infinite loop */ + printf("Terminating cleanly !\n"); + if (conf_log_file != NULL) { + fclose(conf_log_file); + } + if (data_log_fd != 0) { + close(data_log_fd); + } + if (info_log_fd != 0) { + close(info_log_fd); + } close(slave_fd); return 0; } -- 2.43.0