From c710b472d3f8c38279425c4bf0350f4ce68a7db7 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Tue, 16 Jul 2019 23:43:24 +0200 Subject: [PATCH] Aircon daemon: Error resiliency --- aircon/daemon/burrow-aircond.c | 214 +++++++++++++++++++++++---------- 1 file changed, 148 insertions(+), 66 deletions(-) diff --git a/aircon/daemon/burrow-aircond.c b/aircon/daemon/burrow-aircond.c index 629c202..c297a9f 100644 --- a/aircon/daemon/burrow-aircond.c +++ b/aircon/daemon/burrow-aircond.c @@ -16,37 +16,168 @@ #include #include #include +#include #include #include #include "../firmware/registers.h" +/*** MQTT ***/ + static struct mosquitto *mosq; -static modbus_t *modbus; +static bool mqtt_connected; + +static void mqtt_error(const char *operation, int err, bool teardown) +{ + msg(L_ERROR, "Mosquitto: %s failed: error %d", operation, err); + + if (teardown) { + mosquitto_destroy(mosq); + mosq = NULL; + mqtt_connected = false; + } else if (err == MOSQ_ERR_NO_CONN || err == MOSQ_ERR_CONN_REFUSED || err == MOSQ_ERR_CONN_LOST) { + mqtt_connected = false; + } +} static void mqtt_publish(const char *topic, const char *fmt, ...) { va_list args; va_start(args, fmt); - char m[256]; - int l = vsnprintf(m, sizeof(m), fmt, args); - if (mosquitto_publish(mosq, NULL, topic, l, m, 0, true) != MOSQ_ERR_SUCCESS) - msg(L_ERROR, "Mosquitto: publish failed"); + + if (mqtt_connected) { + char m[256]; + int l = vsnprintf(m, sizeof(m), fmt, args); + int err = mosquitto_publish(mosq, NULL, topic, l, m, 0, true); + if (err != MOSQ_ERR_SUCCESS) + mqtt_error("publish", err, false); + } + va_end(args); } -static void mqtt_setup(void) +static void mqtt_log_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int level, const char *message) +{ + // msg(L_INFO, "MQTT(%d): %s", level, message); +} + +static void mqtt_msg_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, const struct mosquitto_message *m); + +static bool mqtt_connect(void) { - if (mosquitto_subscribe(mosq, NULL, "burrow/air/#", 1) != MOSQ_ERR_SUCCESS) - die("Mosquitto: subscribe failed"); + int err; + + if (mqtt_connected) + return true; + + if (!mosq) { + mosq = mosquitto_new("aircond", 1, NULL); + if (!mosq) + die("Mosquitto: initialization failed"); + + mosquitto_log_callback_set(mosq, mqtt_log_callback); + mosquitto_message_callback_set(mosq, mqtt_msg_callback); + + err = mosquitto_will_set(mosq, "status/aircon", 4, "dead", 0, true); + if (err != MOSQ_ERR_SUCCESS) { + mqtt_error("will_set", err, true); + return false; + } + + err = mosquitto_connect(mosq, "127.0.0.1", 1883, 60); + if (err != MOSQ_ERR_SUCCESS) { + mqtt_error("connect", err, true); + return false; + } + } else { + err = mosquitto_reconnect(mosq); + if (err != MOSQ_ERR_SUCCESS) { + mqtt_error("reconnect", err, false); + return false; + } + } + + err = mosquitto_subscribe(mosq, NULL, "burrow/air/#", 1); + if (err != MOSQ_ERR_SUCCESS) { + mqtt_error("subscribe", err, false); + return false; + } + + mqtt_connected = true; mqtt_publish("status/aircon", "ok"); + + return mqtt_connected; } -static void mqtt_log_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int level, const char *message) +/*** MODBUS ***/ + +static modbus_t *modbus; +static bool mb_is_open; + +static void mb_error(const char *operation) { - // msg(L_INFO, "MQTT(%d): %s", level, message); + msg(L_ERROR, "MODBUS: %s failed: %s", operation, modbus_strerror(errno)); + + if (modbus) { + if (mb_is_open) { + modbus_close(modbus); + mb_is_open = false; + } + modbus_free(modbus); + modbus = NULL; + } +} + +static bool mb_connect(void) +{ + if (modbus) + return true; + + // FIXME: Find the right device. Reconnect if needed. + modbus = modbus_new_rtu("/dev/ttyUSB1", 19200, 'E', 8, 1); + if (!modbus) { + mb_error("open"); + return false; + } + + modbus_set_slave(modbus, 42); + + if (modbus_connect(modbus) < 0) { + mb_error("connect"); + return false; + } + + mb_is_open = true; +} + +/*** Main loop ***/ + +static void scan_temperatures(time_t now) +{ + static const char * const temp_names[] = { + "inside-intake", + "inside-exhaust", + "outside-intake", + "outside-exhaust", + "mixed", + }; + + u16 regs[5]; + if (modbus_read_input_registers(modbus, AIRCON_IREG_TEMP_FROM_INSIDE, 5, regs) < 0) { + mb_error("read"); + return; + } + + for (uint i=0; i<5; i++) { + if (regs[i] != 0x8000) { + int t = regs[i]; + if (t >= 0x8000) + t -= 0x10000; + mqtt_publish(stk_printf("burrow/air/%s", temp_names[i]), "%.3f %llu", t / 100., (unsigned long long) now); + } + } } static void mqtt_msg_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, const struct mosquitto_message *m) @@ -81,32 +212,6 @@ static void mqtt_msg_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, c } } -static void scan_temperatures(time_t now) -{ - static const char * const temp_names[] = { - "inside-intake", - "inside-exhaust", - "outside-intake", - "outside-exhaust", - "mixed", - }; - - u16 regs[5]; - if (modbus_read_input_registers(modbus, AIRCON_IREG_TEMP_FROM_INSIDE, 5, regs) < 0) { - msg(L_ERROR, "Modbus read failed: %s", modbus_strerror(errno)); - return; - } - - for (uint i=0; i<5; i++) { - if (regs[i] != 0x8000) { - int t = regs[i]; - if (t >= 0x8000) - t -= 0x10000; - mqtt_publish(stk_printf("burrow/air/%s", temp_names[i]), "%.3f %llu", t / 100., (unsigned long long) now); - } - } -} - static int use_daemon; static int use_debug; @@ -135,43 +240,20 @@ int main(int argc UNUSED, char **argv) if (!use_debug) log_default_stream()->levels &= ~(1U << L_DEBUG); - // FIXME: Find the right device. Reconnect if needed. - modbus = modbus_new_rtu("/dev/ttyUSB1", 19200, 'E', 8, 1); - if (!modbus) - die("Unable to establish Modbus context"); - modbus_set_slave(modbus, 42); - if (modbus_connect(modbus) < 0) - die("Unable to connect to Modbus"); - mosquitto_lib_init(); - mosq = mosquitto_new("aircond", 1, NULL); - if (!mosq) - die("Mosquitto: initialization failed"); - - mosquitto_log_callback_set(mosq, mqtt_log_callback); - mosquitto_message_callback_set(mosq, mqtt_msg_callback); - - if (mosquitto_will_set(mosq, "status/aircon", 4, "dead", 0, true) != MOSQ_ERR_SUCCESS) - die("Mosquitto: unable to set will"); - - if (mosquitto_connect(mosq, "127.0.0.1", 1883, 60) != MOSQ_ERR_SUCCESS) - die("Mosquitto: connect failed"); - - mqtt_setup(); time_t next_run = 0; for (;;) { + if (!mqtt_connect() || !mb_connect()) { + sleep(5); + continue; + } + time_t now = time(NULL); if (now < next_run) { int err = mosquitto_loop(mosq, (next_run - now) * 1000, 1); - if (err == MOSQ_ERR_NO_CONN) { - err = mosquitto_reconnect(mosq); - if (err == MOSQ_ERR_SUCCESS) - mqtt_setup(); - else - msg(L_ERROR, "Mosquitto: cannot reconnect, error %d", err); - } else if (err != MOSQ_ERR_SUCCESS) - msg(L_ERROR, "Mosquitto: loop returned error %d", err); + if (err != MOSQ_ERR_SUCCESS) + mqtt_error("loop", err, false); continue; } -- 2.39.5