]> mj.ucw.cz Git - home-hw.git/commitdiff
Aircon daemon: Error resiliency
authorMartin Mares <mj@ucw.cz>
Tue, 16 Jul 2019 21:43:24 +0000 (23:43 +0200)
committerMartin Mares <mj@ucw.cz>
Tue, 16 Jul 2019 21:43:24 +0000 (23:43 +0200)
aircon/daemon/burrow-aircond.c

index 629c202606d6c5e241868927ffbe45d5e426d1fc..c297a9f0d7a04ae61d27ca45003fd7f73c8d5fe5 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <syslog.h>
+#include <unistd.h>
 
 #include <modbus.h>
 #include <mosquitto.h>
 
 #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;
                }