страницы печатаются Json-данные

У меня ESP32 с веб-сервером, на странице, где я показываю показания датчиков и состояние системы, сейчас показания отображаются хорошо, проблема возникает, когда я хочу показать состояние системы.

Сейчас я тестирую поведение только со страницей /encender. Состояние системы отправляется в файле JSON только для обновления элементов, содержащих состояние системы, но вместо обновления этих элементов HTML страница исчезает, и веб-обозреватель показывает мне ошибку пустого ответа.

Вот мой фактический код, относящийся к моей проблеме:

Код, относящийся к странице /encender (элемент) в backend.cpp:

server.on("/encender", HTTP_GET,[](AsyncWebServerRequest *request){
    digitalWrite(ledPinSistemaApagado,LOW);
    digitalWrite(ledPinSistemaEncendido,HIGH);
    if(ultimaPaginaCargada == "/encender");
    {
      Serial.println("No se envía la página /encender, solo se actualizará el estado del sistema.");
      request->send(200);
      return;
    }
    ultimaPaginaCargada = "/encender";
    cuentaAcceso = true;
    request->send(SPIFFS,"/index.html", String(), false, Procesador);
  });

код в backend.cpp, связанный с обновлением состояния функции, выглядит следующим образом

server.on("/data-estado", HTTP_GET, [](AsyncWebServerRequest *request){
    if(!request->authenticate(usuarioHTTP, claveHTTP) && !cuentaAcceso) {
        return request->requestAuthentication("Ingreso al Sistema");
    };
    Serial.println("Enviando la data de estado.");
    sistemaEstado["estado-sistema"] = ledEstado;
    sistemaEstado["modo-sistema"] = modoSistema;
    String json = "";
    serializeJsonPretty(sistemaEstado, json);
    Serial.println(json);
    ultimaPaginaCargada = "/data-estado";
    cuentaAcceso = true;
    request->send(200, "application/json", json);
    json = String();
    Serial.println("Enviada la data de estado.");
});

В данный момент, если переменная ùltimaPaginaCargada равна "/encender", это означает, что страница /encender была ранее загружена, поэтому она отправляет только "200" в веб-обозреватель, в противном случае "/encender" не был ранее загружен, и эта страница отправляется.

Ниже приведен код моего app.js:

function Actualiza_Estado() {
    console.log("Entrando a Actualiza_Estado().");
    var xxhr = new XMLHttpRequest();
    if(this.readyState == 4 && this.status == 200)
    {
        console.log("Analizando los componentes reibidos en Actualiza_Estado()");
        var estadoWeb = JSON.parse(this.responseText);
        console.log(estadoWeb);
        var estadoEnc = document.getElementById("estado-sistema");
        estadoEnc.innerHTML = "<p>Estado Sistema: " + "<strong>" + `${estadoWeb.estado-sistema}` + "</strong>";
        var modoEnc = document.getElementById("modo-sistema");
        modoEnc.innerHTML = "<p>Modo Sistema: " + "<strong>" + `${modoEnc.modo-sistema}` + "</strong>";
        console.log("Enviados los componentes a actualizar a la pantalla");
    }
    else
    {
        console.log("Contenido no cargado en Actualiza_Estado()");
        
    }
    xxhr.open("HTTP_GET", "/data-estado", true);
    xxhr.send();
    console.log("Solicitada la actualización del estado del sistema");
};

// Функции для отображения уровня резервуара на странице дисплея.

function Lee_Nivel_Tanque() {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
        if(this.readyState == 4 && this.status == 200)
        {
            var volTanqueWeb = JSON.parse(this.responseText);
            console.log(volTanqueWeb);
            var volActualWeb = document.getElementById("mask01");
            volActualWeb.innerHTML = volTanqueWeb.colAgua.toFixed(2);
        }
        else
        {
            console.log("Contenido no cargado en Lee_Nivel_Tanque()");
        }
    };
    xhr.open("HTTP_GET", "/data-tanque", true);
    xhr.send();
    console.log("Solicitada actualización del tanque.");
};

// Функции для отправки времени и даты в окно

function Fecha_Hora() {
    let hoyFecha = new Date();
    let hoyHora = hoyFecha.toLocaleTimeString();
    let dia = hoyFecha.getDate();
    let mes = hoyFecha.getMonth() + 1;
    let anio = hoyFecha.getFullYear();
    let diaSemana = hoyFecha.getDay();
    dia = ('0' + dia).slice(-2);
    mes = ('0' + mes).slice(-2);
    let semana = ['DOMINGO', 'LUNES', 'MARTES', 'MIÉRCOLES', 'JUEVES', 'VIERNES', 'SÁBADO'];
    let muestraSemana = semana[diaSemana];
    let fechaCompleta = `${muestraSemana}-${dia}-${mes}-${anio}`;
    document.getElementById("la-fecha").innerHTML = fechaCompleta;
    document.getElementById("la-hora").innerHTML = hoyHora;
    console.log(fechaCompleta + '->' + hoyHora);
};

window.onload = function() {
    let intervaloVolumen;
    let intervaloFecha;
    intervaloFecha = setInterval(Fecha_Hora, 1000);
    if((window.location.pathname === "/index") ||
        (window.location.pathname === "/apagar") ||
        (window.location.pathname === "/encender") ||
        (window.location.pathname === "/auto") ||
        (window.location.pathname === "/manual"))
    {
        //Обновить_Состояние();
        intervaloVolumen = setInterval(Lee_Nivel_Tanque, 20000);
    }
    else
    {
        clearInterval(intervaloVolumen);
        intervaloVolumen = null;
    }
}

var datoEstado = document.getElementsByClassName("boton-control");
datoEstado.addEventListener("click", function(event) {
    event.preventDefault();
    Actualiza_Estado();
});

выше функций, которые, по моему мнению, имеют значение, это Àctualiza_Estado() и var datoEstado, datoEstado по сути является прослушивателем, который, когда я нажимаю на любую из кнопок, созданных с помощью элементов <a>, вызывает Àctualiza_Estado() для запроса JSON-файла с состоянием системы.

В функции window.onload(), если загружена какая-либо из кнопок или основная страница, она определяет интервал обновления параметра для считывания с датчика и каждые 20 секунд запрашивает JSON-файл с этими данными (это работает отлично).

заключается в том, что в функции server.on(), связанной со страницей /encender, я заставляю программу отправлять ответ 200 на запрос, чтобы заставить код ESP32 отправлять только файл JSON, вызывая функцию Actualiza_Estado() с запросами /data-estado, но, как я уже сказал, веб-обозреватель, похоже, получает пустой ответ.

Я внес изменение в функцию server.on(), связанную со страницей /encender в backend.cpp, чтобы принудительно отправлять JSON-файл напрямую оттуда, как показано ниже

server.on("/encender", HTTP_GET,[](AsyncWebServerRequest *request){
    digitalWrite(ledPinSistemaApagado,LOW);
    digitalWrite(ledPinSistemaEncendido,HIGH);
    if(ultimaPaginaCargada == "/encender");
    {
    Serial.println("Enviando la data de estado.");
    sistemaEstado["estado-sistema"] = ledEstado;
    sistemaEstado["modo-sistema"] = modoSistema;
    String json = "";
    serializeJsonPretty(sistemaEstado, json);
    Serial.println(json);
    ultimaPaginaCargada = "/data-estado";
    cuentaAcceso = true;
    request->send(200, "application/json", json);
    json = String();
    }
    ultimaPaginaCargada = "/encender";
    cuentaAcceso = true;
    request->send(SPIFFS,"/index.html", String(), false, Procesador);
});

В приведенном выше случае веб-браузер получает файл Json и отображает данные, но на пустой черной странице, а также показывает отформатированный файл Json.

Я не знаю, что происходит, потому что метод файла Json работает очень хорошо при использовании его для считывания показаний датчика с помощью Lee_Nivel_Tanque().

Что я могу сделать, чтобы решить эту проблему?

, 👍2

Обсуждение

estadoWeb.estado-sistema должен быть estadoWeb["estado-sistema"], @dandavis

Проблема, с которой вы столкнулись, скорее всего, связана с обработкой request->send(200); в конечной точке /encender. Вы уже решили проблему?, @tepalia

@tepalia да, я смогу решить эту проблему, основываясь на всех твоих советах, @vram


1 ответ


0

Я не разбирал весь вопрос. Просто смотрю на код, там есть довольно много вопросов, которые заслуживают внимания.

В бэкэнде для /encender у вас есть

    if(ultimaPaginaCargada == "/encender");
    {
    // ...

Обратите внимание на точку с запятой сразу после условия. В C++ голая точка с запятой является пустым оператором (no-op). Здесь этот пустой оператор является телом оператора if. Следующий за ним блок в фигурных скобках выполняется безусловно. Скорее всего, это не то, что вы имеете в виду.

        request->send(200);

Не отправляйте пустой ответ 200. Если вы хотите сказать клиенту «ваш запрос успешно обработан, но мне нечего отправить вы», вам следует отправить ответ 204 No Content.

    ultimaPaginaCargada = "/encender";

Веб-API должен быть без состояния. Если вам нужно запомнить последний страница загружена, весь ваш протокол, скорее всего, имеет изъяны.

В функции JavaScript Actualiza_Estado():

    var xxhr = new XMLHttpRequest();
    if (this.readyState == 4 && this.status == 200) {

Вторая строка здесь неуместна: она должна быть внутри onreadystatechange обработчик. Посмотрите, как написан Lee_Nivel_Tanque().


Редактировать: Еще несколько замечаний.

if(ultimaPaginaCargada == "/encender");

Ранее я писал: «Если вам нужно запомнить последнюю загруженную страницу, ваш Весь протокол, скорее всего, имеет изъяны». Вы могли неправильно это понять. точка. Проблема не в том, что сервер помнит, какая страница была загружено последним. Проблема в том, что его поведение зависит от этого информация. Ответ сервера не должен зависеть от того, какая страница была загружено последним.

request->send(SPIFFS, "/index.html", String(), false, Procesador);

Это выглядит подозрительно. Я не уверен, что полностью понимаю ваш подход, но, Из того немногого, что вы показали, мне кажется, что вы строите Веб-интерфейс пользователя для некоторого устройства в виде одностраничное приложение. При таком подходе у вас есть:

  1. Единственный URL (обычно корневой: /), который доставляет пользователя интерфейс в виде статического HTML-документа, возможно с встроенный JavaScript.
  2. Ноль или более URL-адресов, которые предоставляют статические ресурсы, требуемые этим документ: JavaScript, изображения, шрифты…
  3. Одна или несколько конечных точек, которые позволяют вам взаимодействовать с устройством либо извлечение динамических данных в формате JSON (конечные точки GET) или изменение состояние устройства (конечные точки POST).

При таком подходе единственный случай, когда вы можете предоставить HTML-документ в ответ на запрос GET /. Конечная точка динамических данных должна никогда не доставляйте index.html.

xxhr.open("HTTP_GET", "/data-estado", true);

Метод HTTP следует записывать как "GET", а не "HTTP_GET".

,

Привет @Edgar Bonet, спасибо за исправления, я не заметил, что оставил там точку с запятой, и спасибо за совет по поводу API без сохранения состояния, я внесу некоторые изменения и дам вам знать., @vram

и я должен отметить, что для Actualiza_Estado() мне не нужен временной интервал, кроме того, я знаю, что вы имеете в виду запоминание последней загруженной страницы в моем backend.cpp, в то время мне не нужно было запоминать страницы, я просто хотел сохранить последнюю загруженную страницу. Далее, функция Actualiza_Estado() предназначена для отображения на веб-странице состояния аппаратной части моего проекта, включена она или выключена и т. д., @vram

и спасибо за совет по ответу 204, @vram