]> mj.ucw.cz Git - home-hw.git/commitdiff
Rainbow: LEDs have optional owners
authorMartin Mares <mj@ucw.cz>
Sun, 27 Feb 2022 13:19:13 +0000 (14:19 +0100)
committerMartin Mares <mj@ucw.cz>
Sun, 27 Feb 2022 13:19:13 +0000 (14:19 +0100)
rainbow/daemon/burrow-rainbowd.c

index 95cbfe1548f241a76fddccdfce789492fa4007fb..7080076019f2879e3554605bf1ca5107f25f9189 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 #include <ucw/lib.h>
+#include <ucw/clists.h>
 #include <ucw/log.h>
 #include <ucw/opt.h>
 #include <ucw/string.h>
@@ -31,17 +32,64 @@ typedef unsigned int uint;
 
 #include "../firmware/interface.h"
 
+/*** LED status ***/
+
+struct client {
+       cnode n;
+       char *name;
+       int status;             // 0=unknown, 1=running, -1=dead
+};
+
+static clist clients;
+static struct client *null_client;
+
 static mtx_t led_mutex;
 static cnd_t led_cond;
 static bool led_refresh;
 
 struct led {
-    double r, g, b;
+       double r, g, b;
+       struct client *sender;
 };
 
 static struct led leds[NPIX_NUM_LEDS];
 static double led_brightness = 1;
 
+static struct client *find_client(const char *name)
+{
+       CLIST_FOR_EACH(struct client *, c, clients)
+               if (!strcmp(c->name, name))
+                       return c;
+
+       struct client *c = xmalloc_zero(sizeof(*c));
+       clist_add_tail(&clients, &c->n);
+       c->name = xstrdup(name);
+       return c;
+}
+
+static void led_init(void) {
+       clist_init(&clients);
+       null_client = find_client("");
+
+       for (uint i=0; i < NPIX_NUM_LEDS; i++)
+               leds[i].sender = null_client;
+
+       mtx_init(&led_mutex, mtx_plain);
+       cnd_init(&led_cond);
+}
+
+static void led_begin_update(void)
+{
+       mtx_lock(&led_mutex);
+}
+
+static void led_end_update(void)
+{
+       led_refresh = 1;
+       cnd_broadcast(&led_cond);
+       mtx_unlock(&led_mutex);
+}
+
 /*** MQTT ***/
 
 static struct mosquitto *mosq;
@@ -68,9 +116,11 @@ static void mqtt_conn_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED,
        if (!status) {
                msg(L_DEBUG, "MQTT: Connection established");
                mqtt_connected = true;
-               mqtt_publish("status/rainbow", "ok");
                if (mosquitto_subscribe(mosq, NULL, "burrow/lights/rainbow/#", 1) != MOSQ_ERR_SUCCESS)
                        die("Mosquitto: subscribe failed");
+               if (mosquitto_subscribe(mosq, NULL, "status/#", 1) != MOSQ_ERR_SUCCESS)
+                       die("Mosquitto: subscribe failed");
+               mqtt_publish("status/rainbow", "ok");
        } else if (mqtt_connected) {
                msg(L_DEBUG, "MQTT: Connection lost");
                mqtt_connected = false;
@@ -82,55 +132,102 @@ static void mqtt_log_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, i
        msg(L_DEBUG, "MQTT(%d): %s", level, message);
 }
 
-static void mqtt_msg_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, const struct mosquitto_message *m)
+static void msg_status(const char *topic, const char *key, const char *val)
 {
-       char val[256];
-       if (m->payloadlen >= sizeof(val) - 1) {
-               msg(L_ERROR, "Invalid value for topic %s", m->topic);
+       if (!*key) {
+               msg(L_ERROR, "Unknown status topic %s", topic);
                return;
        }
-       memcpy(val, m->payload, m->payloadlen);
-       val[m->payloadlen] = 0;
-       msg(L_DEBUG, "MQTT < %s %s", m->topic, val);
 
-       static const char px[] = "burrow/lights/rainbow/";
-       if (!str_has_prefix(m->topic, px))
-               return;
-
-       const char *top = m->topic + strlen(px);
-       if (!strcmp(top, "brightness")) {
-               double b = atof(val);
-               if (!(b >= 0 && b <= 1)) {
-                       msg(L_ERROR, "Invalid value of %s: %s", m->topic, val);
-                       return;
+       int status;
+       if (!*val)
+               status = 0;
+       else if (!strcmp(val, "ok"))
+               status = 1;
+       else
+               status = -1;
+
+       led_begin_update();
+       struct client *c = find_client(key);
+       c->status = status;
+       if (status < 0) {
+               for (uint i=0; i < NPIX_NUM_LEDS; i++) {
+                       struct led *led = &leds[i];
+                       if (led->sender == c) {
+                               led->r = led->g = led->b = 0;
+                               led->sender = null_client;
+                       }
                }
-               mtx_lock(&led_mutex);
-               led_brightness = b;
-               led_refresh = 1;
-               cnd_broadcast(&led_cond);
-               mtx_unlock(&led_mutex);
+       }
+       led_end_update();
+}
+
+static void msg_brightness(const char *topic, const char *val)
+{
+       double b = atof(val);
+       if (!(b >= 0 && b <= 1)) {
+               msg(L_ERROR, "Invalid value of %s: %s", topic, val);
                return;
        }
 
+       led_begin_update();
+       led_brightness = b;
+       led_end_update();
+}
+
+static void msg_rainbow(const char *topic, const char *key, const char *val)
+{
+       if (!strcmp(key, "brightness"))
+               return msg_brightness(topic, val);
+
        uint index;
-       if (str_to_uint(&index, m->topic + strlen(px), NULL, 10 | STN_WHOLE) || index >= NPIX_NUM_LEDS) {
-               msg(L_ERROR, "Unsupported topic: %s", m->topic);
+       if (str_to_uint(&index, key, NULL, 10 | STN_WHOLE) || index >= NPIX_NUM_LEDS) {
+               msg(L_ERROR, "Unknown topic: %s", topic);
                return;
        }
 
        double r, g, b;
-       if (sscanf(val, "%lf%lf%lf", &r, &g, &b) != 3 || !(r >= 0 && r <= 1) || !(g >= 0 && g <= 1) || !(b >= 0 && b <= 1)) {
-               msg(L_ERROR, "Invalid value of %s: %s", m->topic, val);
+       char sender[strlen(val) + 1];
+       sender[0] = 0;
+
+       if (!*val) {
+               r = g = b = 0;
+       } else if (sscanf(val, "%lf%lf%lf %s", &r, &g, &b, sender) < 3 || !(r >= 0 && r <= 1) || !(g >= 0 && g <= 1) || !(b >= 0 && b <= 1)) {
+               msg(L_ERROR, "Invalid value of %s: %s", topic, val);
                return;
        }
 
-       mtx_lock(&led_mutex);
-       leds[index].r = r;
-       leds[index].g = g;
-       leds[index].b = b;
-       led_refresh = 1;
-       cnd_broadcast(&led_cond);
-       mtx_unlock(&led_mutex);
+       led_begin_update();
+       struct led *led = &leds[index];
+       struct client *client = find_client(sender);
+       if (client->status < 0) {
+               msg(L_ERROR, "LED update from a dead client %s", client->name);
+       } else {
+               led->r = r;
+               led->g = g;
+               led->b = b;
+               led->sender = client;
+       }
+       led_end_update();
+}
+
+static void mqtt_msg_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, const struct mosquitto_message *m)
+{
+       char val[256];
+       if (m->payloadlen >= sizeof(val) - 1) {
+               msg(L_ERROR, "Invalid value for topic %s", m->topic);
+               return;
+       }
+       memcpy(val, m->payload, m->payloadlen);
+       val[m->payloadlen] = 0;
+       msg(L_DEBUG, "MQTT < %s %s", m->topic, val);
+
+       static const char px_status[] = "status/";
+       static const char px_rainbow[] = "burrow/lights/rainbow/";
+       if (str_has_prefix(m->topic, px_status))
+               msg_status(m->topic, m->topic + strlen(px_status), val);
+       else if (str_has_prefix(m->topic, px_rainbow))
+               msg_rainbow(m->topic, m->topic + strlen(px_rainbow), val);
 }
 
 static void mqtt_init(void)
@@ -228,9 +325,12 @@ static int npix_build_packet(void)
        byte *pkt = npix_packet;
 
        for (int i=0; i < NPIX_NUM_LEDS; i++) {
-               *pkt++ = (int)(leds[i].r * led_brightness * 255);
-               *pkt++ = (int)(leds[i].g * led_brightness * 255);
-               *pkt++ = (int)(leds[i].b * led_brightness * 255);
+               struct led *led = &leds[i];
+               struct client *sender = led->sender;
+               msg(L_DEBUG, "LED #%d: r=%.3f g=%.3f b=%.3f client=%s status=%d", i, led->r, led->g, led->b, sender->name, sender->status);
+               *pkt++ = (int)(led->r * led_brightness * 255);
+               *pkt++ = (int)(led->g * led_brightness * 255);
+               *pkt++ = (int)(led->b * led_brightness * 255);
        }
 
        return pkt - npix_packet;
@@ -266,9 +366,7 @@ int main(int argc UNUSED, char **argv)
        if (!use_debug)
                log_default_stream()->levels &= ~(1U << L_DEBUG);
 
-       mtx_init(&led_mutex, mtx_plain);
-       cnd_init(&led_cond);
-
+       led_init();
        mqtt_init();
        init_usb();