diff --git a/plugins/protocol_esp32_lws_group.c b/plugins/protocol_esp32_lws_group.c new file mode 100644 index 00000000..4b20e6fa --- /dev/null +++ b/plugins/protocol_esp32_lws_group.c @@ -0,0 +1,239 @@ +/* + * ESP32 Group protocol handler + * + * Copyright (C) 2017 Andy Green + * + * 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 +#include +#include + +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 \ + } +