diff --git a/src/App/LedModFixture.h b/src/App/LedModFixture.h index 186b636e..e8102d48 100644 --- a/src/App/LedModFixture.h +++ b/src/App/LedModFixture.h @@ -66,7 +66,7 @@ class LedModFixture:public SysModule { // ui->setComment(var, "Click to enlarge"); return true; case f_LoopFun: { - var["interval"] = max(eff->fixture.nrOfLeds * web->ws->count()/200, 16U)*10; //interval in ms * 10, not too fast //from cs to ms + var["interval"] = max(eff->fixture.nrOfLeds * web->ws.count()/200, 16U)*10; //interval in ms * 10, not too fast //from cs to ms web->sendDataWs([this](AsyncWebSocketMessageBuffer * wsBuf) { byte* buffer; diff --git a/src/Sys/SysModSystem.cpp b/src/Sys/SysModSystem.cpp index 25ea7277..e28ffbe1 100644 --- a/src/Sys/SysModSystem.cpp +++ b/src/Sys/SysModSystem.cpp @@ -48,7 +48,7 @@ void SysModSystem::setup() { ui->initButton(parentVar, "reboot", false, [](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun case f_ChangeFun: - web->ws->closeAll(1012); + web->ws.closeAll(1012); // mdls->reboot(); //not working yet // long dly = millis(); diff --git a/src/Sys/SysModWeb.cpp b/src/Sys/SysModWeb.cpp index 634dd8da..00f2964c 100644 --- a/src/Sys/SysModWeb.cpp +++ b/src/Sys/SysModWeb.cpp @@ -26,32 +26,7 @@ //https://techtutorialsx.com/2018/08/24/esp32-web-server-serving-html-from-file-system/ //https://randomnerdtutorials.com/esp32-async-web-server-espasyncwebserver-library/ -WebServer * SysModWeb::server = nullptr; -WebSocket * SysModWeb::ws = nullptr; - -bool SysModWeb::clientsChanged = false; - -unsigned8 SysModWeb::sendWsCounter = 0; -unsigned16 SysModWeb::sendWsTBytes = 0; -unsigned16 SysModWeb::sendWsBBytes = 0; -unsigned8 SysModWeb::recvWsCounter = 0; -unsigned16 SysModWeb::recvWsBytes = 0; -unsigned8 SysModWeb::sendUDPCounter = 0; -unsigned16 SysModWeb::sendUDPBytes = 0; -unsigned8 SysModWeb::recvUDPCounter = 0; -unsigned16 SysModWeb::recvUDPBytes = 0; - -SemaphoreHandle_t SysModWeb::wsMutex = xSemaphoreCreateMutex(); - SysModWeb::SysModWeb() :SysModule("Web") { - #ifdef STARMOD_USE_Psychic - ws = new WebSocket(); - server = new WebServer(); - #else - ws = new WebSocket("/ws"); - server = new WebServer(80); - #endif - //CORS compatiblity DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Origin"), "*"); DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Methods"), "*"); @@ -74,7 +49,7 @@ void SysModWeb::setup() { ui->initNumber(tableVar, "clNr", UINT16_MAX, 0, 999, true, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun case f_ValueFun: { - unsigned8 rowNr = 0; for (auto client:ws->getClients()) + unsigned8 rowNr = 0; for (auto client:ws.getClients()) mdl->setValue(var, client->id(), rowNr++); return true; } case f_UIFun: @@ -85,7 +60,7 @@ void SysModWeb::setup() { ui->initText(tableVar, "clIp", nullptr, 16, true, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun case f_ValueFun: { - unsigned8 rowNr = 0; for (auto client:ws->getClients()) + unsigned8 rowNr = 0; for (auto client:ws.getClients()) mdl->setValue(var, JsonString(client->remoteIP().toString().c_str(), JsonString::Copied), rowNr++); return true; } case f_UIFun: @@ -96,7 +71,7 @@ void SysModWeb::setup() { ui->initCheckBox(tableVar, "clIsFull", UINT16_MAX, true, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun case f_ValueFun: { - unsigned8 rowNr = 0; for (auto client:ws->getClients()) + unsigned8 rowNr = 0; for (auto client:ws.getClients()) mdl->setValue(var, client->queueIsFull(), rowNr++); return true; } case f_UIFun: @@ -107,7 +82,7 @@ void SysModWeb::setup() { ui->initSelect(tableVar, "clStatus", UINT16_MAX, true, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun case f_ValueFun: { - unsigned8 rowNr = 0; for (auto client:ws->getClients()) + unsigned8 rowNr = 0; for (auto client:ws.getClients()) mdl->setValue(var, client->status(), rowNr++); return true; } case f_UIFun: @@ -125,7 +100,7 @@ void SysModWeb::setup() { ui->initNumber(tableVar, "clLength", UINT16_MAX, 0, WS_MAX_QUEUED_MESSAGES, true, [this](JsonObject var, unsigned8 rowNr, unsigned8 funType) { switch (funType) { //varFun case f_ValueFun: { - unsigned8 rowNr = 0; for (auto client:ws->getClients()) + unsigned8 rowNr = 0; for (auto client:ws.getClients()) mdl->setValue(var, client->queueLength(), rowNr++); return true; } case f_UIFun: @@ -213,37 +188,37 @@ void SysModWeb::loop1s() { void SysModWeb::reboot() { USER_PRINTF("SysModWeb reboot\n"); - ws->closeAll(1012); + ws.closeAll(1012); } void SysModWeb::connectedChanged() { if (mdls->isConnected) { #ifdef STARMOD_USE_Psychic - server->listen(80); + server.listen(80); - ws->onOpen(wsEventOpen); - ws->onFrame(wsEventFrame); - ws->onClose(wsEventClose); + ws.onOpen(wsEventOpen); + ws.onFrame(wsEventFrame); + ws.onClose(wsEventClose); - server->on("/ws")->setHandler(ws); + server.on("/ws")->setHandler(&ws); #else - ws->onEvent(wsEvent); - server->addHandler(ws); - server->begin(); + ws.onEvent( [this](WebSocket * server, WebClient * client, AwsEventType type, void * arg, uint8_t *data, size_t len) {wsEvent(server, client, type, arg, data, len);}); + server.addHandler(&ws); + server.begin(); #endif - server->on("/", HTTP_GET, serveIndex); + server.on("/", HTTP_GET, [this](WebRequest *request) {serveIndex(request);}); //serve json calls - server->on("/json", HTTP_GET, serveJson); + server.on("/json", HTTP_GET, [this](WebRequest *request) {serveJson(request);}); - server->addHandler(new AsyncCallbackJsonWebHandler("/json", jsonHandler)); + server.addHandler(new AsyncCallbackJsonWebHandler("/json", [this](WebRequest *request, JsonVariant &json){jsonHandler(request, json);})); - server->on("/update", HTTP_POST, [](WebRequest *request) {}, serveUpdate); - server->on("/file", HTTP_GET, serveFiles); - server->on("/upload", HTTP_POST, [](WebRequest *request) {}, serveUpload); + server.on("/update", HTTP_POST, nullptr, [this](WebRequest *request, const String& filename, size_t index, byte *data, size_t len, bool final) {serveUpdate(request, filename, index, data, len, final);}); + server.on("/file", HTTP_GET, [this](WebRequest *request) {serveFiles(request);}); + server.on("/upload", HTTP_POST, nullptr, [this](WebRequest *request, const String& filename, size_t index, byte *data, size_t len, bool final) {serveUpload(request, filename, index, data, len, final);}); // USER_PRINTF("%s server (re)started\n", name); //causes crash for some reason... USER_PRINTF("server (re)started\n"); @@ -259,7 +234,7 @@ void SysModWeb::connectedChanged() { // https://github.com/me-no-dev/ESPAsyncWebServer/blob/master/examples/ESP_AsyncFSBrowser/ESP_AsyncFSBrowser.ino void SysModWeb::wsEvent(WebSocket * ws, WebClient * client, AwsEventType type, void * arg, byte *data, size_t len){ - // if (!ws->count()) { + // if (!ws.count()) { // USER_PRINT_Async("wsEvent no clients\n"); // return; // } @@ -334,12 +309,12 @@ void SysModWeb::wsEvent(WebSocket * ws, WebClient * client, AwsEventType type, v //message is comprised of multiple frames or the frame is split into multiple packets if(info->index == 0){ // if(info->num == 0) - // USER_PRINT_Async("ws[%s][%u] %s-message start\n", ws->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); - // USER_PRINT_Async("ws[%s][%u] frame[%u] start[%llu]\n", ws->url(), client->id(), info->num, info->len); + // USER_PRINT_Async("ws[%s][%u] %s-message start\n", ws.url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); + // USER_PRINT_Async("ws[%s][%u] frame[%u] start[%llu]\n", ws.url(), client->id(), info->num, info->len); USER_PRINTF("💀"); } - // USER_PRINT_Async("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", ws->url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len); + // USER_PRINT_Async("ws[%s][%u] frame[%u] %s[%llu - %llu]: ", ws.url(), client->id(), info->num, (info->message_opcode == WS_TEXT)?"text":"binary", info->index, info->index + len); USER_PRINTF("💀"); if(info->opcode == WS_TEXT){ @@ -357,10 +332,10 @@ void SysModWeb::wsEvent(WebSocket * ws, WebClient * client, AwsEventType type, v USER_PRINT_Async("%s\n",msg.c_str()); if ((info->index + len) == info->len) { - // USER_PRINT_Async("ws[%s][%u] frame[%u] end[%llu]\n", ws->url(), client->id(), info->num, info->len); + // USER_PRINT_Async("ws[%s][%u] frame[%u] end[%llu]\n", ws.url(), client->id(), info->num, info->len); USER_PRINTF("👻"); if(info->final){ - // USER_PRINT_Async("ws[%s][%u] %s-message end\n", ws->url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); + // USER_PRINT_Async("ws[%s][%u] %s-message end\n", ws.url(), client->id(), (info->message_opcode == WS_TEXT)?"text":"binary"); USER_PRINTF("☠️"); if(info->message_opcode == WS_TEXT) client->text("I got your text message"); @@ -398,7 +373,7 @@ void SysModWeb::wsEvent(WebSocket * ws, WebClient * client, AwsEventType type, v //pong message was received (in response to a ping request maybe) // printClient("WS pong", client); //crashes! // USER_PRINT_Async("WS pong\n"); - // USER_PRINT_Async("ws[%s][%u] pong[%u]: %s\n", ws->url(), client->id(), len, (len)?(char*)data:""); + // USER_PRINT_Async("ws[%s][%u] pong[%u]: %s\n", ws.url(), client->id(), len, (len)?(char*)data:""); USER_PRINT_Async("ws[%s][%u] pong[%u]: \n", ws->url(), client->id(), len);//, (len)?(char*)data:""); } } @@ -413,28 +388,24 @@ void SysModWeb::sendDataWs(JsonVariant json, WebClient * client) { //https://kcwong-joe.medium.com/passing-a-function-as-a-parameter-in-c-a132e69669f6 void SysModWeb::sendDataWs(std::function fill, size_t len, bool isBinary, WebClient * client) { - if (!ws) { - USER_PRINT_Async("sendDataWs no ws\n"); - return; - } xSemaphoreTake(wsMutex, portMAX_DELAY); - ws->cleanupClients(); //only if above threshold + ws.cleanupClients(); //only if above threshold - if (ws->count()) { + if (ws.count()) { if (len > 8192) USER_PRINTF("program error: sendDataWs BufferLen too high !!!%d\n", len); - AsyncWebSocketMessageBuffer * wsBuf = ws->makeBuffer(len); //assert failed: block_trim_free heap_tlsf.c:371 (block_is_free(block) && "block must be free"), AsyncWebSocket::makeBuffer(unsigned int) + AsyncWebSocketMessageBuffer * wsBuf = ws.makeBuffer(len); //assert failed: block_trim_free heap_tlsf.c:371 (block_is_free(block) && "block must be free"), AsyncWebSocket::makeBuffer(unsigned int) if (wsBuf) { wsBuf->lock(); fill(wsBuf); //function parameter - for (auto loopClient:ws->getClients()) { + for (auto loopClient:ws.getClients()) { if (!client || client == loopClient) { - if (loopClient->status() == WS_CONNECTED && !loopClient->queueIsFull()) { //WS_MAX_QUEUED_MESSAGES / ws->count() / 2)) { //binary is lossy + if (loopClient->status() == WS_CONNECTED && !loopClient->queueIsFull()) { //WS_MAX_QUEUED_MESSAGES / ws.count() / 2)) { //binary is lossy if ((!isBinary || loopClient->queueLength() <= 3)) { isBinary?loopClient->binary(wsBuf): loopClient->text(wsBuf); sendWsCounter++; @@ -447,19 +418,19 @@ void SysModWeb::sendDataWs(std::function fi else { printClient("sendDataWs client full or not connected", loopClient); // USER_PRINTF("sendDataWs client full or not connected\n"); - ws->cleanupClients(); //only if above threshold - ws->_cleanBuffers(); + ws.cleanupClients(); //only if above threshold + ws._cleanBuffers(); } } } wsBuf->unlock(); - ws->_cleanBuffers(); + ws._cleanBuffers(); } else { USER_PRINT_Async("sendDataWs WS buffer allocation failed\n"); - ws->closeAll(1013); //code 1013 = temporary overload, try again later - ws->cleanupClients(0); //disconnect ALL clients to release memory - ws->_cleanBuffers(); + ws.closeAll(1013); //code 1013 = temporary overload, try again later + ws.cleanupClients(0); //disconnect ALL clients to release memory + ws._cleanBuffers(); } } @@ -469,7 +440,7 @@ void SysModWeb::sendDataWs(std::function fi //add an url to the webserver to listen to void SysModWeb::serveIndex(WebRequest *request) { - USER_PRINT_Async("Webserver: server->on serveIndex csdata %d-%d (%s)", PAGE_index, PAGE_index_L, request->url().c_str()); + USER_PRINT_Async("Webserver: server.on serveIndex csdata %d-%d (%s)", PAGE_index, PAGE_index_L, request->url().c_str()); // if (captivePortal(request)) return; @@ -600,7 +571,7 @@ void SysModWeb::jsonHandler(WebRequest *request, JsonVariant json) { } void SysModWeb::clientsToJson(JsonArray array, bool nameOnly, const char * filter) { - for (auto client:ws->getClients()) { + for (auto client:ws.getClients()) { if (nameOnly) { array.add(JsonString(client->remoteIP().toString().c_str(), JsonString::Copied)); } else { diff --git a/src/Sys/SysModWeb.h b/src/Sys/SysModWeb.h index 7ee79f76..2a19bad4 100644 --- a/src/Sys/SysModWeb.h +++ b/src/Sys/SysModWeb.h @@ -37,18 +37,25 @@ class SysModWeb:public SysModule { public: - static WebSocket *ws;// = nullptr; - static SemaphoreHandle_t wsMutex;// = xSemaphoreCreateMutex(); - - static unsigned8 sendWsCounter;// = 0; - static unsigned16 sendWsTBytes;// = 0; - static unsigned16 sendWsBBytes;// = 0; - static unsigned8 recvWsCounter;// = 0; - static unsigned16 recvWsBytes;// = 0; - static unsigned8 sendUDPCounter;// = 0; - static unsigned16 sendUDPBytes;// = 0; - static unsigned8 recvUDPCounter;// = 0; - static unsigned16 recvUDPBytes;// = 0; + #ifdef STARMOD_USE_Psychic + WebSocket ws = WebSocket(); + WebServer server = WebServer(); + #else + WebSocket ws = WebSocket("/ws"); + WebServer server = WebServer(80); + #endif + + SemaphoreHandle_t wsMutex = xSemaphoreCreateMutex(); + + unsigned8 sendWsCounter = 0; + unsigned16 sendWsTBytes = 0; + unsigned16 sendWsBBytes = 0; + unsigned8 recvWsCounter = 0; + unsigned16 recvWsBytes = 0; + unsigned8 sendUDPCounter = 0; + unsigned16 sendUDPBytes = 0; + unsigned8 recvUDPCounter = 0; + unsigned16 recvUDPBytes = 0; SysModWeb(); @@ -60,23 +67,23 @@ class SysModWeb:public SysModule { void connectedChanged(); - static void wsEvent(WebSocket * ws, WebClient * client, AwsEventType type, void * arg, byte *data, size_t len); + void wsEvent(WebSocket * ws, WebClient * client, AwsEventType type, void * arg, byte *data, size_t len); //send json to client or all clients - static void sendDataWs(JsonVariant json = JsonVariant(), WebClient * client = nullptr); - static void sendDataWs(std::function fill, size_t len, bool isBinary, WebClient * client = nullptr); + void sendDataWs(JsonVariant json = JsonVariant(), WebClient * client = nullptr); + void sendDataWs(std::function fill, size_t len, bool isBinary, WebClient * client = nullptr); //add an url to the webserver to listen to - static void serveIndex(WebRequest *request); + void serveIndex(WebRequest *request); //mdl and WLED style state and info - static void serveJson(WebRequest *request); + void serveJson(WebRequest *request); // curl -F 'data=@fixture1.json' 192.168.8.213/upload - static void serveUpload(WebRequest *request, const String& filename, size_t index, byte *data, size_t len, bool final); + void serveUpload(WebRequest *request, const String& filename, size_t index, byte *data, size_t len, bool final); // curl -s -F "update=@/Users/ewoudwijma/Developer/GitHub/ewowi/StarMod/.pio/build/esp32dev/firmware.bin" 192.168.8.102/update /dev/null & - static void serveUpdate(WebRequest *request, const String& filename, size_t index, byte *data, size_t len, bool final); - static void serveFiles(WebRequest *request); + void serveUpdate(WebRequest *request, const String& filename, size_t index, byte *data, size_t len, bool final); + void serveFiles(WebRequest *request); //processJsonUrl handles requests send in javascript using fetch and from a browser or curl //try this !!!: curl -X POST "http://192.168.121.196/json" -d '{"pin2":false}' -H "Content-Type: application/json" @@ -86,7 +93,7 @@ class SysModWeb:public SysModule { //curl -X POST "http://192.168.8.152/json" -d '{"nrOfLeds":2000}' -H "Content-Type: application/json" //handle "v" and processJson (on /json) - static void jsonHandler(WebRequest *request, JsonVariant json); + void jsonHandler(WebRequest *request, JsonVariant json); //Is this an IP? bool isIp(String str) { @@ -158,7 +165,7 @@ class SysModWeb:public SysModule { JsonObject getResponseObject(); void sendResponseObject(WebClient * client = nullptr); - static void printClient(const char * text, WebClient * client) { + void printClient(const char * text, WebClient * client) { USER_PRINTF("%s client: %d ...%d q:%d l:%d s:%d (#:%d)\n", text, client?client->id():-1, client?client->remoteIP()[3]:-1, client->queueIsFull(), client->queueLength(), client->status(), client->server()->count()); //status: { WS_DISCONNECTED, WS_CONNECTED, WS_DISCONNECTING } } @@ -166,9 +173,7 @@ class SysModWeb:public SysModule { private: bool modelUpdated = false; - static bool clientsChanged;// = false;; - - static WebServer *server;// = nullptr; + bool clientsChanged = false; JsonDocument *responseDocLoopTask = nullptr; JsonDocument *responseDocAsyncTCP = nullptr; diff --git a/src/testOutput.txt b/src/testOutput.txt index 25977a53..a06379b8 100644 --- a/src/testOutput.txt +++ b/src/testOutput.txt @@ -166,7 +166,7 @@ setValue var clStatus[0] value null not array, creating setValue var clLength[0] value null not array, creating sendDataWs client full or not connected client: 1 ...126 q:0 l:12 s:1 (#:1) sendDataWs client full or not connected client: 1 ...126 q:0 l:12 s:1 (#:1) -αWebserver: server->on addUrl / text/html csdata 1061164428-20841 (/)α! +αWebserver: server.on addUrl / text/html csdata 1061164428-20841 (/)α! αWS Client disconnected client: 1 ...0 q:1 l:0 s:2 (#:0) αWS client connected client: 2 ...126 q:0 l:0 s:1 (#:1) αWS event data client: 2 ...126 q:0 l:15 s:1 (#:1)