plugins: group
authorAndy Green <andy@warmcat.com>
Tue, 16 May 2017 12:21:47 +0000 (20:21 +0800)
committerAndy Green <andy@warmcat.com>
Tue, 16 May 2017 12:21:47 +0000 (20:21 +0800)
plugins/protocol_esp32_lws_group.c [new file with mode: 0644]

diff --git a/plugins/protocol_esp32_lws_group.c b/plugins/protocol_esp32_lws_group.c
new file mode 100644 (file)
index 0000000..4b20e6f
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * ESP32 Group protocol handler
+ *
+ * Copyright (C) 2017 Andy Green <andy@warmcat.com>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation:
+ *  version 2.1 of the License.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *  MA  02110-1301  USA*
+ *
+ */
+#include <string.h>
+#include <nvs.h>
+#include <esp_ota_ops.h>
+
+typedef enum {
+       GROUP_STATE_NONE,
+       GROUP_STATE_INITIAL,
+       GROUP_STATE_MEMBERS,
+       GROUP_STATE_FINAL
+} group_state;
+
+struct per_session_data__lws_group {
+       struct per_session_data__lws_group *next;
+       group_state group_state;
+
+       struct lws_group_member *member;
+
+       unsigned char subsequent:1;
+       unsigned char changed_partway:1;
+};
+
+struct per_vhost_data__lws_group {
+       struct per_session_data__lws_group *live_pss_list;
+       struct lws_context *context;
+       struct lws_vhost *vhost;
+       const struct lws_protocols *protocol;
+       int count_live_pss;
+};
+
+static void render_ip4(char *dest, int len, uint8_t *ip)
+{
+       snprintf(dest, len, "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
+}
+
+
+
+static int
+callback_lws_group(struct lws *wsi, enum lws_callback_reasons reason,
+                  void *user, void *in, size_t len)
+{
+       struct per_session_data__lws_group *pss =
+                       (struct per_session_data__lws_group *)user;
+       struct per_vhost_data__lws_group *vhd =
+                       (struct per_vhost_data__lws_group *)
+                       lws_protocol_vh_priv_get(lws_get_vhost(wsi),
+                                       lws_get_protocol(wsi));
+       char buffer[1024 + LWS_PRE], ipv4[20];
+       char *start = buffer + LWS_PRE - 1, *p = start,
+                     *end = buffer + sizeof(buffer) - 1;
+       struct lws_group_member *mbr;
+       int n, m;
+
+       switch (reason) {
+
+       case LWS_CALLBACK_PROTOCOL_INIT:
+               vhd = lws_protocol_vh_priv_zalloc(lws_get_vhost(wsi),
+                               lws_get_protocol(wsi),
+                               sizeof(struct per_vhost_data__lws_group));
+               vhd->context = lws_get_context(wsi);
+               vhd->protocol = lws_get_protocol(wsi);
+               vhd->vhost = lws_get_vhost(wsi);
+               break;
+
+       case LWS_CALLBACK_PROTOCOL_DESTROY:
+               if (!vhd)
+                       break;
+               break;
+
+       case LWS_CALLBACK_ESTABLISHED:
+               lwsl_notice("%s: ESTABLISHED\n", __func__);
+               vhd->count_live_pss++;
+               pss->next = vhd->live_pss_list;
+               vhd->live_pss_list = pss;
+               pss->group_state = GROUP_STATE_INITIAL;
+               lws_callback_on_writable(wsi);
+               break;
+
+       case LWS_CALLBACK_SERVER_WRITEABLE:
+
+               switch (pss->group_state) {
+
+               case GROUP_STATE_NONE:
+                       /* fallthru */
+
+               case GROUP_STATE_INITIAL:
+
+                       p += snprintf((char *)p, end - p,
+                                     "{\n"
+                                     " \"group\":\"%s\","
+                                     " \"members\":[\n",
+                                     lws_esp32.group);
+
+                       n = LWS_WRITE_TEXT | LWS_WRITE_NO_FIN;
+                       pss->group_state = GROUP_STATE_MEMBERS;
+                       pss->subsequent = 0;
+                       pss->changed_partway = 0;
+                       pss->member = lws_esp32.first;
+                       break;
+
+               case GROUP_STATE_MEMBERS:
+
+                       /* confirm pss->member is still in the list... */
+
+                       mbr = lws_esp32.first;
+                       while (mbr && mbr != pss->member)
+                               mbr = mbr->next;
+
+                       if (!mbr) { /* no longer exists... */
+                               if (lws_esp32.first || pss->member)
+                                       pss->changed_partway = 1;
+                               *p++ = ' ';
+                               pss->member = NULL;
+
+                               /*
+                                * finish the list where we got to, then
+                                * immediately reissue it
+                                */
+                       }
+                       
+                       while (end - p > 100 && pss->member) {
+
+                               if (pss->subsequent)
+                                       *p++ = ',';
+
+                               pss->subsequent = 1;
+                               render_ip4(ipv4, sizeof(ipv4), (uint8_t *)&pss->member->addr);
+
+                               p += snprintf((char *)p, end - p,
+                                             " {\n"
+                                             "  \"mac\":\"%s\",\n"
+                                             "  \"model\":\"%s\",\n"
+                                             "  \"role\":\"%s\",\n"
+                                             "  \"width\":\"%d\",\n"
+                                             "  \"height\":\"%d\",\n"
+                                             "  \"ipv4\":\"%s\"\n"
+                                             " }\n",
+                                               pss->member->mac,
+                                               pss->member->model,
+                                               pss->member->role,
+                                               pss->member->width,
+                                               pss->member->height,
+                                               ipv4
+                                             );
+                               pss->member = pss->member->next;
+                       }
+
+                       lwsl_notice("%s\n", p);
+
+                       n = LWS_WRITE_CONTINUATION | LWS_WRITE_NO_FIN;
+                       if (!pss->member)
+                               pss->group_state = GROUP_STATE_FINAL;
+                       break;
+
+               case GROUP_STATE_FINAL:
+                       n = LWS_WRITE_CONTINUATION;
+                       p += sprintf((char *)p, "],\n \"discard\":\"%d\"}\n",
+                                       pss->changed_partway);
+                       if (pss->changed_partway)
+                               pss->group_state = GROUP_STATE_INITIAL;
+                       else
+                               pss->group_state = GROUP_STATE_NONE;
+                       break;
+               default:
+                       return 0;
+               }
+//             lwsl_notice("issue: %d (%d)\n", p - start, n);
+               m = lws_write(wsi, (unsigned char *)start, p - start, n);
+               if (m < 0) {
+                       lwsl_err("ERROR %d writing to di socket\n", m);
+                       return -1;
+               }
+
+               if (pss->group_state != GROUP_STATE_NONE)
+                       lws_callback_on_writable(wsi);
+
+               break;
+
+       case LWS_CALLBACK_RECEIVE:
+               {
+                       break;
+               }
+
+       case LWS_CALLBACK_CLOSED:
+               {
+                       struct per_session_data__lws_group **p = &vhd->live_pss_list;
+
+                       while (*p) {
+                               if ((*p) == pss) {
+                                       *p = pss->next;
+                                       continue;
+                               }
+
+                               p = &((*p)->next);
+                       }
+
+                       vhd->count_live_pss--;
+               }
+               break;
+
+       case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
+               /* called when our wsi user_space is going to be destroyed */
+               break;
+
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+#define LWS_PLUGIN_PROTOCOL_LWS_GROUP \
+       { \
+               "lws-group", \
+               callback_lws_group, \
+               sizeof(struct per_session_data__lws_group), \
+               1024, 0, NULL, 900 \
+       }
+