/
ellersseer
/
MyCheat
Обзор
Документация
Войти
/
ellersseer
/
MyCheat
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
2
CI/CD
Аналитика
Безопасность
master
src/web_server.cpp
261 строка
8 KB
Dmitry Zhiltsov
feat(logs): Добавить логи в WEB
20 фев 2026, 20:39
20 фев 2026, 20:39
dae6bcf
Код
Авторство
О чём код?
#include "web_server.h" #include "app_state.h" #include "sensor_api.h" #include "debug_log.h" #include "log_buffer.h" #include "web_html_index.h" #include "web_html_logs.h" #include <ArduinoJson.h> #include <AsyncJson.h> // SSE event source for log streaming static AsyncEventSource logSSE("/api/logs/stream"); // ============================================ // Helpers // ============================================ static void countWebRequest() { if (xSemaphoreTake(statsMutex, pdMS_TO_TICKS(10)) == pdTRUE) { stats.webRequests++; xSemaphoreGive(statsMutex); } } // ============================================ // Handler: POST /api/sensors and /api/sensors/{id} // ============================================ static void handleSensorsPost(AsyncWebServerRequest *request, JsonVariant &json) { countWebRequest(); String url = request->url(); JsonObject obj = json.as<JsonObject>(); // Single sensor: /api/sensors/{id} if (url.startsWith("/api/sensors/")) { int sensorIndex = url.substring(13).toInt(); if (sensorIndex < 0 || sensorIndex >= NUM_SENSORS) { request->send(404, "application/json", "{\"error\":\"Sensor not found\"}"); return; } bool hasTemp = obj["temperature"].is<float>(); bool hasEnabled = obj["enabled"].is<bool>(); if (!hasTemp && !hasEnabled) { request->send(400, "application/json", "{\"error\":\"Missing temperature or enabled field\"}"); return; } if (hasEnabled) { SensorCommand_t cmd; cmd.sensorIndex = sensorIndex; cmd.type = obj["enabled"].as<bool>() ? SensorCommandType::ENABLE_SENSOR : SensorCommandType::DISABLE_SENSOR; xQueueSend(sensorCommandQueue, &cmd, pdMS_TO_TICKS(100)); } if (hasTemp) { SensorCommand_t cmd; cmd.type = SensorCommandType::SET_TEMPERATURE; cmd.sensorIndex = sensorIndex; cmd.temperature = obj["temperature"].as<float>(); xQueueSend(sensorCommandQueue, &cmd, pdMS_TO_TICKS(100)); } vTaskDelay(pdMS_TO_TICKS(50)); request->send(200, "application/json", getSensorJson(sensorIndex)); return; } // Bulk update: /api/sensors if (obj["temperatures"].is<JsonArray>()) { JsonArray temps = obj["temperatures"].as<JsonArray>(); SensorCommand_t cmd; cmd.type = SensorCommandType::SET_ALL_TEMPS; float currentTemps[NUM_SENSORS]; getAllTemperatures(currentTemps); int i = 0; for (JsonVariant v : temps) { if (i < NUM_SENSORS) { cmd.allTemperatures[i] = v.as<float>(); i++; } } for (; i < NUM_SENSORS; i++) { cmd.allTemperatures[i] = currentTemps[i]; } xQueueSend(sensorCommandQueue, &cmd, pdMS_TO_TICKS(100)); } vTaskDelay(pdMS_TO_TICKS(50)); request->send(200, "application/json", getAllSensorsJson()); } // ============================================ // Handler: POST /api/config/sensors // ============================================ static void handleSensorConfigPost(AsyncWebServerRequest *request, JsonVariant &json) { JsonObject obj = json.as<JsonObject>(); bool changed = false; if (obj["staleTimeoutMinutes"].is<int>()) { sensorSettings.staleTimeoutMinutes = obj["staleTimeoutMinutes"].as<uint16_t>(); settingsManager.saveStaleTimeout(sensorSettings.staleTimeoutMinutes); changed = true; } JsonDocument response; response["success"] = true; response["staleTimeoutMinutes"] = sensorSettings.staleTimeoutMinutes; if (changed) { response["message"] = "Sensor configuration updated"; } String responseStr; serializeJson(response, responseStr); request->send(200, "application/json", responseStr); } // ============================================ // Web Server Setup // ============================================ void setupWebServer() { logCapture.println("[Web] Setting up server..."); // Root - status page (served from PROGMEM) webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { countWebRequest(); request->send_P(200, "text/html", INDEX_HTML); }); // API: Get all sensors webServer.on("/api/sensors", HTTP_GET, [](AsyncWebServerRequest *request) { countWebRequest(); request->send(200, "application/json", getAllSensorsJson()); }); // API: Set temperatures (handles both /api/sensors and /api/sensors/{id}) auto *tempHandler = new AsyncCallbackJsonWebHandler("/api/sensors", handleSensorsPost); webServer.addHandler(tempHandler); // API: Get single sensor webServer.on("^\\/api\\/sensors\\/([0-9]+)$", HTTP_GET, [](AsyncWebServerRequest *request) { countWebRequest(); int sensorIndex = request->pathArg(0).toInt(); if (sensorIndex >= 0 && sensorIndex < NUM_SENSORS) { request->send(200, "application/json", getSensorJson(sensorIndex)); } else { request->send(404, "application/json", "{\"error\":\"Sensor not found\"}"); } }); // API: System status webServer.on("/api/status", HTTP_GET, [](AsyncWebServerRequest *request) { countWebRequest(); request->send(200, "application/json", getStatusJson()); }); // API: Debug routes - more specific routes MUST be registered first webServer.on("/api/debug/coredump/erase", HTTP_POST, [](AsyncWebServerRequest *request) { debugLog.eraseCoreDump(); request->send(200, "application/json", "{\"status\":\"ok\",\"message\":\"Core dump erased\"}"); }); webServer.on("/api/debug/reset", HTTP_POST, [](AsyncWebServerRequest *request) { debugLog.resetStats(); request->send(200, "application/json", "{\"status\":\"ok\",\"message\":\"Debug stats reset\"}"); }); webServer.on("/api/debug/events", HTTP_GET, [](AsyncWebServerRequest *request) { countWebRequest(); request->send(200, "application/json", debugLog.getEventsJson()); }); webServer.on("/api/debug", HTTP_GET, [](AsyncWebServerRequest *request) { countWebRequest(); request->send(200, "application/json", debugLog.getDebugJson()); }); // API: Get sensor configuration webServer.on("/api/config/sensors", HTTP_GET, [](AsyncWebServerRequest *request) { JsonDocument doc; doc["staleTimeoutMinutes"] = sensorSettings.staleTimeoutMinutes; JsonArray sensorsArr = doc["sensors"].to<JsonArray>(); for (int i = 0; i < NUM_SENSORS; i++) { JsonObject s = sensorsArr.add<JsonObject>(); s["index"] = i; s["enabled"] = sensorStates[i].enabled; s["status"] = sensorStatusToString(sensorStates[i].status); } String response; serializeJson(doc, response); request->send(200, "application/json", response); }); // API: Set sensor configuration auto *sensorConfigHandler = new AsyncCallbackJsonWebHandler("/api/config/sensors", handleSensorConfigPost); webServer.addHandler(sensorConfigHandler); // Log viewer page webServer.on("/logs", HTTP_GET, [](AsyncWebServerRequest *request) { countWebRequest(); request->send_P(200, "text/html", LOGS_HTML); }); // SSE: Log stream — MUST be registered BEFORE /api/logs // (ESPAsyncWebServer prefix-matches "/api/logs" to "/api/logs/stream") webServer.addHandler(&logSSE); logCapture.setEventSource(&logSSE); // API: Get log buffer as plain text webServer.on("/api/logs", HTTP_GET, [](AsyncWebServerRequest *request) { countWebRequest(); request->send(200, "text/plain", logCapture.getContent()); }); webServer.onNotFound([](AsyncWebServerRequest *request) { request->send(404, "application/json", "{\"error\":\"Not found\"}"); }); webServer.begin(); logCapture.printf("[Web] Server started on port %d\n", WEB_SERVER_PORT); }