/* * 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 \ }