AutoConnectUpdate-rc2

Abolished WebSocket in-process notification for update progress notification. Its method consumes too much memory on the ESP8266. Replace with Ajax.
pull/123/head
Hieromon Ikasamo 6 years ago
parent f0dde87064
commit 40cb4cf8e0
  1. 35
      src/AutoConnectDefs.h
  2. 283
      src/AutoConnectUpdate.cpp
  3. 66
      src/AutoConnectUpdate.h
  4. 23
      src/AutoConnectUpdatePage.h

@ -90,8 +90,9 @@
#define AUTOCONNECT_URI_SUCCESS AUTOCONNECT_URI "/success"
#define AUTOCONNECT_URI_FAIL AUTOCONNECT_URI "/fail"
#define AUTOCONNECT_URI_UPDATE AUTOCONNECT_URI "/update"
#define AUTOCONNECT_URI_UPDATE_ACT AUTOCONNECT_URI "/update_act"
#define AUTOCONNECT_URI_UPDATE_RESULT AUTOCONNECT_URI "/update_result"
#define AUTOCONNECT_URI_UPDATE_ACT AUTOCONNECT_URI "/update_act"
#define AUTOCONNECT_URI_UPDATE_PROGRESS AUTOCONNECT_URI "/update_progress"
#define AUTOCONNECT_URI_UPDATE_RESULT AUTOCONNECT_URI "/update_result"
// Time-out limitation when AutoConnect::begin [ms]
#ifndef AUTOCONNECT_TIMEOUT
@ -138,7 +139,7 @@
#define AUTOCONNECT_SSIDPAGEUNIT_LINES 5
#endif // !AUTOCONNECT_SSIDPAGEUNIT_LINES
// SPI transfer speed for SD
// SPI transfer speed for SD [Hz]
#ifndef AUTOCONNECT_SD_SPEED
#define AUTOCONNECT_SD_SPEED 4000000
#endif // !AUTOCONNECT_SD_SPEED
@ -154,20 +155,39 @@
#define AUTOCONNECT_JSONPSRAM_SIZE (16* 1024)
#endif // !AUTOCONNECT_JSONPSRAM_SIZE
// Available HTTP port number for the update
// Available HTTP port number for the update [ms]
#ifndef AUTOCONNECT_UPDATE_PORT
#define AUTOCONNECT_UPDATE_PORT 8000
#endif // !AUTOCONNECT_UPDATE_PORT
// HTTP client timeout limitation for the update
// HTTP client timeout limitation for the update [ms]
#ifndef AUTOCONNECT_UPDATE_TIMEOUT
#define AUTOCONNECT_UPDATE_TIMEOUT 8000
#endif // !AUTOCONNECT_UPDATE_TIMEOUT
// Maximum wait time until transitioning AutoConnectUpdate dialog page [ms]
#ifndef AUTOCONNECT_UPDATE_DURATION
#define AUTOCONNECT_UPDATE_DURATION 180000
#endif // !AUTOCONNECT_UPDATE_DURATION
// Interval time of progress status periodical inquiry [ms]
#ifndef AUTOCONNECT_UPDATE_INTERVAL
#define AUTOCONNECT_UPDATE_INTERVAL 400
#endif // !AUTOCONNECT_UPDATE_INTERVAL
// Wait timer for rebooting after updated
#ifndef AUTOCONNECT_UPDATE_WAITFORREBOOT
#define AUTOCONNECT_UPDATE_WAITFORREBOOT 9000
#endif // !AUTOCONNECT_UPDATE_WAITFORREBOOT
// A signal value that the board dependent LED turns on.
// As a typical example, the ON signal of built-in LED such as the
// NodeMCU is LOW and the HIGH for the NodeMCU-32S as another example.
#ifndef AUTOCONNECT_UPDATE_LEDON
// #define AUTOCONNECT_UPDATE_LEDON HIGH
#define AUTOCONNECT_UPDATE_LEDON LOW
#endif // !AUTOCONNECT_UPDATE_LEDON
// URIs of the behaviors owned by the update server
#ifndef AUTOCONNECT_UPDATE_CATALOG
#define AUTOCONNECT_UPDATE_CATALOG "/_catalog"
@ -179,11 +199,6 @@
#define AUTOCONNECT_UPDATE_CATALOG_JSONBUFFER_SIZE 256
#endif // !AUTOCONNECT_UPDATE_CATALOG_JSONBUFFER_SIZE
// Default WebSocket port for the update progress measur
#ifndef AUTOCONNECT_WEBSOCKETPORT
#define AUTOCONNECT_WEBSOCKETPORT 81
#endif // !AUTOCONNECT_WEBSOCKETPORT
// Explicitly avoiding unused warning with token handler of PageBuilder
#define AC_UNUSED(expr) do { (void)(expr); } while (0)

@ -30,7 +30,7 @@
* queries from the AutoConnectUpdateAct class. The catalog script accepts
* the queries such as '/catalog?op=list&path='.
* - op:
* An op parameter speicies the query operation. In the current
* An op parameter specifies the query operation. In the current
* version, available query operation is a list only.
* - list:
* The query operation list responds with a list of available sketch
@ -90,15 +90,9 @@ AC_HAS_FUNC(onProgress);
template<typename T>
typename std::enable_if<AutoConnectUtil::has_func_onProgress<T>::value, AutoConnectUpdateAct::AC_UPDATEDIALOG_t>::type onProgress(T& updater, UpdateVariedClass::THandlerFunction_Progress fn) {
#if defined(ARDUINO_ARCH_ESP32) || (defined(ARDUINO_ARCH_ESP8266) && (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC))
updater.onProgress(fn);
AC_DBG("Updater keeps callback\n");
return AutoConnectUpdateAct::UPDATEDIALOG_METER;
#else
(void)(updater);
(void)(fn);
return AutoConnectUpdateAct::UPDATEDIALOG_LOADER;
#endif
}
template<typename T>
@ -109,15 +103,23 @@ typename std::enable_if<!AutoConnectUtil::has_func_onProgress<T>::value, AutoCon
}
}
/**
* Definitions of notification commands to synchronize update processing
* with the Web client.
*/
#define UPDATE_NOTIFY_START "#s"
#define UPDATE_NOTIFY_PROGRESS "#p"
#define UPDATE_NOTIFY_END "#e"
#define UPDATE_NOTIFY_REBOOT "#r"
/**
* A destructor. Release the update processing dialogue page generated
* as AutoConnectAux.
*/
AutoConnectUpdateAct::~AutoConnectUpdateAct() {
_catalog.reset(nullptr);
_progress.reset(nullptr);
_result.reset(nullptr);
_ws.reset(nullptr);
_auxCatalog.reset(nullptr);
_auxProgress.reset(nullptr);
_auxResult.reset(nullptr);
}
/**
@ -130,44 +132,35 @@ AutoConnectUpdateAct::~AutoConnectUpdateAct() {
void AutoConnectUpdateAct::attach(AutoConnect& portal) {
AutoConnectAux* updatePage;
updatePage = new AutoConnectAux(String(FPSTR(_auxCatalog.uri)), String(FPSTR(_auxCatalog.title)), _auxCatalog.menu);
_buildAux(updatePage, &_auxCatalog, lengthOf(_elmCatalog));
_catalog.reset(updatePage);
updatePage = new AutoConnectAux(String(FPSTR(_pageCatalog.uri)), String(FPSTR(_pageCatalog.title)), _pageCatalog.menu);
_buildAux(updatePage, &_pageCatalog, lengthOf(_elmCatalog));
_auxCatalog.reset(updatePage);
updatePage = new AutoConnectAux(String(FPSTR(_auxProgress.uri)), String(FPSTR(_auxProgress.title)), _auxProgress.menu);
_buildAux(updatePage, &_auxProgress, lengthOf(_elmProgress));
_progress.reset(updatePage);
updatePage = new AutoConnectAux(String(FPSTR(_pageProgress.uri)), String(FPSTR(_pageProgress.title)), _pageProgress.menu);
_buildAux(updatePage, &_pageProgress, lengthOf(_elmProgress));
_auxProgress.reset(updatePage);
updatePage = new AutoConnectAux(String(FPSTR(_auxResult.uri)), String(FPSTR(_auxResult.title)), _auxResult.menu);
_buildAux(updatePage, &_auxResult, lengthOf(_elmResult));
_result.reset(updatePage);
updatePage = new AutoConnectAux(String(FPSTR(_pageResult.uri)), String(FPSTR(_pageResult.title)), _pageResult.menu);
_buildAux(updatePage, &_pageResult, lengthOf(_elmResult));
_auxResult.reset(updatePage);
_catalog->on(std::bind(&AutoConnectUpdateAct::_onCatalog, this, std::placeholders::_1, std::placeholders::_2), AC_EXIT_AHEAD);
_progress->on(std::bind(&AutoConnectUpdateAct::_onUpdate, this, std::placeholders::_1, std::placeholders::_2), AC_EXIT_AHEAD);
_result->on(std::bind(&AutoConnectUpdateAct::_onResult, this, std::placeholders::_1, std::placeholders::_2), AC_EXIT_AHEAD);
_auxCatalog->on(std::bind(&AutoConnectUpdateAct::_onCatalog, this, std::placeholders::_1, std::placeholders::_2), AC_EXIT_AHEAD);
_auxProgress->on(std::bind(&AutoConnectUpdateAct::_onUpdate, this, std::placeholders::_1, std::placeholders::_2), AC_EXIT_AHEAD);
_auxResult->on(std::bind(&AutoConnectUpdateAct::_onResult, this, std::placeholders::_1, std::placeholders::_2), AC_EXIT_AHEAD);
portal.join(*_catalog.get());
portal.join(*_progress.get());
portal.join(*_result.get());
_status = UPDATE_IDLE;
// Attach this to the AutoConnectUpdateAct
portal._update.reset(this);
AC_DBG("AutoConnectUpdate attached\n");
if (WiFi.status() == WL_CONNECTED)
enable();
portal.join(*_auxCatalog.get());
portal.join(*_auxProgress.get());
portal.join(*_auxResult.get());
// Register the callback to inform the update progress
_dialog = AutoConnectUtil::onProgress<UpdateVariedClass>(Update, std::bind(&AutoConnectUpdateAct::_inProgress, this, std::placeholders::_1, std::placeholders::_2));
// Adjust the client dialog pattern according to the callback validity
// of the UpdateClass.
AutoConnectElement* loader = _progress->getElement(String(F("loader")));
AutoConnectElement* enable_loader = _progress->getElement(String(F("enable_loader")));
AutoConnectElement* progress_meter = _progress->getElement(String(F("progress_meter")));
AutoConnectElement* inprogress_meter = _progress->getElement(String(F("inprogress_meter")));
AutoConnectElement* progress_loader = _progress->getElement(String(F("progress_loader")));
AutoConnectElement* inprogress_loader = _progress->getElement(String(F("inprogress_loader")));
AutoConnectElement* loader = _auxProgress->getElement(String(F("loader")));
AutoConnectElement* progress_meter = _auxProgress->getElement(String(F("progress_meter")));
AutoConnectElement* progress_loader = _auxProgress->getElement(String(F("progress_loader")));
AutoConnectElement* enable_loader = _auxProgress->getElement(String(F("enable_loader")));
AutoConnectElement* inprogress_meter = _auxProgress->getElement(String(F("inprogress_meter")));
switch (_dialog) {
case UPDATEDIALOG_LOADER:
progress_meter->enable =false;
@ -175,11 +168,23 @@ void AutoConnectUpdateAct::attach(AutoConnect& portal) {
break;
case UPDATEDIALOG_METER:
loader->enable = false;
enable_loader->enable =false;
progress_loader->enable =false;
inprogress_loader->enable = false;
enable_loader->enable =false;
break;
}
// Attach this to the AutoConnectUpdateAct
portal._update.reset(this);
AC_DBG("AutoConnectUpdate attached\n");
if (WiFi.status() == WL_CONNECTED)
enable();
// Attach the update progress monitoring handler
_webServer = &(portal.host());
_webServer->on(String(F(AUTOCONNECT_URI_UPDATE_PROGRESS)), HTTP_ANY, std::bind(&AutoConnectUpdateAct::_progress, this));
// Reset the update progress status
_status = UPDATE_IDLE;
}
/**
@ -188,8 +193,8 @@ void AutoConnectUpdateAct::attach(AutoConnect& portal) {
*/
void AutoConnectUpdateAct::disable(const bool activate) {
_enable = activate;
if (_catalog) {
_catalog->menu(false);
if (_auxCatalog) {
_auxCatalog->menu(false);
AC_DBG("AutoConnectUpdate disabled\n");
}
}
@ -201,12 +206,19 @@ void AutoConnectUpdateAct::disable(const bool activate) {
void AutoConnectUpdateAct::enable(void) {
_enable = true;
_status = UPDATE_IDLE;
if (_catalog) {
_catalog->menu(WiFi.status() == WL_CONNECTED);
if (_auxCatalog) {
_auxCatalog->menu(WiFi.status() == WL_CONNECTED);
AC_DBG("AutoConnectUpdate enabled\n");
}
}
/**
* An entry point of the process loop as AutoConnect::handleClient.
* The handleClient function of the AutoConnect that later accompanied
* the AutoConnectUpdate class will invoke this entry.
* This entry point will be called from the process loop of handleClient
* function only if the class is associated with the AutoConnect class.
*/
void AutoConnectUpdateAct::handleUpdate(void) {
// Activate the update menu conditional with WiFi connected.
if (!isEnable() && _enable) {
@ -220,21 +232,8 @@ void AutoConnectUpdateAct::handleUpdate(void) {
// execute it accordingly. It is only this process point that
// requests update processing.
if (_status == UPDATE_START) {
AC_DBG("Waiting for WS connection\n");
unsigned long tm = millis();
while (!_wsConnected) {
if (millis() - tm > AUTOCONNECT_TIMEOUT) {
AC_DBG("WebSocket client connection timeout, update ignored\n");
break;
}
_ws->loop(); // Crawl the connection request.
yield();
}
// Launch the update
if (_wsConnected)
update();
else
_status = UPDATE_IDLE;
_status = UPDATE_PROGRESS;
update();
}
else if (_status == UPDATE_RESET) {
AC_DBG("Restart on %s updated...\n", _binName.c_str());
@ -257,13 +256,9 @@ AC_UPDATESTATUS_t AutoConnectUpdateAct::update(void) {
// Start update
String uriBin = uri + '/' + _binName;
if (_binName.length()) {
AC_DBG("%s:%d/%s update in progress...", host.c_str(), port, uriBin.c_str());
#if defined(ARDUINO_ARCH_ESP8266)
t_httpUpdate_return ret = HTTPUpdateClass::update(host, port, uriBin);
#elif defined(ARDUINO_ARCH_ESP32)
WiFiClient wifiClient;
AC_DBG("%s:%d/%s update in progress...", host.c_str(), port, uriBin.c_str());
t_httpUpdate_return ret = HTTPUpdateClass::update(wifiClient, host, port, uriBin);
#endif
switch (ret) {
case HTTP_UPDATE_FAILED:
_status = UPDATE_FAIL;
@ -279,8 +274,6 @@ AC_UPDATESTATUS_t AutoConnectUpdateAct::update(void) {
AC_DBG_DUMB(" completed\n");
break;
}
// Request the client to close the WebSocket.
_ws->sendTXT(_wsClient, "#e");
}
else {
AC_DBG("An update has not specified");
@ -335,6 +328,20 @@ void AutoConnectUpdateAct::_buildAux(AutoConnectAux* aux, const AutoConnectUpdat
}
}
/**
* An update callback function in HTTPUpdate::update.
* This callback handler acts as an HTTPUpdate::update callback and
* sends the updated amount over the web socket to advance the progress
* of the progress meter displayed in the browser.
* @param amount Already transferred size.
* @param size Total size of the binary to update.
*/
void AutoConnectUpdateAct::_inProgress(size_t amount, size_t size) {
_amount = amount;
_binSize = size;
_webServer->handleClient();
}
/**
* AUTOCONNECT_URI_UPDATE page handler.
* It queries the update server for cataloged sketch binary and
@ -347,7 +354,8 @@ void AutoConnectUpdateAct::_buildAux(AutoConnectAux* aux, const AutoConnectUpdat
*/
String AutoConnectUpdateAct::_onCatalog(AutoConnectAux& catalog, PageArgument& args) {
AC_UNUSED(args);
HTTPClient httpClient;
WiFiClient wifiClient;
HTTPClient httpClient;
// Reallocate available firmwares list.
_binName = String("");
@ -364,11 +372,11 @@ String AutoConnectUpdateAct::_onCatalog(AutoConnectAux& catalog, PageArgument& a
// Throw a query to the update server and parse the response JSON
// document. After that, display the bin type file name contained in
// its JSON document as available updaters to the page.
if (httpClient.begin(host, port, qs)) {
if (httpClient.begin(wifiClient, host, port, qs)) {
int responseCode = httpClient.GET();
if (responseCode == HTTP_CODE_OK) {
JsonVariant jb;
// JsonVariant jb;
bool parse;
char beginOfList[] = "[";
char endOfEntry[] = ",";
@ -437,6 +445,7 @@ String AutoConnectUpdateAct::_onCatalog(AutoConnectAux& catalog, PageArgument& a
AC_DBG("%s\n", caption.value.c_str());
}
_status = UPDATE_IDLE;
return String("");
}
@ -449,26 +458,13 @@ String AutoConnectUpdateAct::_onCatalog(AutoConnectAux& catalog, PageArgument& a
*/
String AutoConnectUpdateAct::_onUpdate(AutoConnectAux& progress, PageArgument& args) {
AC_UNUSED(args);
// launch the WebSocket server
_ws.reset(new WebSocketsServer(AUTOCONNECT_WEBSOCKETPORT));
if (_ws) {
_ws->begin();
_ws->onEvent(std::bind(&AutoConnectUpdateAct::_wsEvent, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
else
AC_DBG("WebSocketsServer allocation failed\n");
// Constructs the dialog page.
AutoConnectElement* binName = progress.getElement(String(F("binname")));
_binName = _catalog->getElement<AutoConnectRadio>(String(F("firmwares"))).value();
_binName = _auxCatalog->getElement<AutoConnectRadio>(String(F("firmwares"))).value();
binName->value = _binName;
AutoConnectElement* url = progress.getElement(String("url"));
url->value = host + ':' + port;
AutoConnectElement* wsurl = progress.getElement(String(F("wsurl")));
wsurl->value = "ws://" + WiFi.localIP().toString() + ':' + AUTOCONNECT_WEBSOCKETPORT;
AC_DBG("Cast WS %s\n", wsurl->value.c_str());
_wsConnected = false;
_status = UPDATE_START;
return String("");
}
@ -485,19 +481,6 @@ String AutoConnectUpdateAct::_onResult(AutoConnectAux& result, PageArgument& arg
String resColor;
bool restart = false;
if (_ws) {
_ws->loop();
while (_wsConnected) {
IPAddress ipAny;
if (_ws->remoteIP(_wsClient) == ipAny)
_wsConnected = false;
else
_ws->close();
yield();
}
_ws.reset(nullptr);
}
switch (_status) {
case UPDATE_SUCCESS:
resForm = String(F(" sucessfully updated. restart..."));
@ -518,48 +501,84 @@ String AutoConnectUpdateAct::_onResult(AutoConnectAux& result, PageArgument& arg
resultElm.value = _binName + resForm;
resultElm.style = String(F("font-size:120%;color:")) + resColor;
result.getElement<AutoConnectElement>(String(F("restart"))).enable = restart;
if (restart)
_status = UPDATE_RESET;
return String("");
}
/**
* An update callback function in HTTPUpdate::update.
* This callback handler acts as an HTTPUpdate::update callback and
* sends the updated amount over the web socket to advance the progress
* of the progress meter displayed in the browser.
* @param amount Already transferred size.
* @param size Total size of the binary to update.
* A handler for notifying the client of the progress of update processing.
* This handler specifies the URI behavior defined as THndlerFunc of
* ESP8266 WebServer (WebServer for ESP32).
* Usually, that URI is /_ac/update_progress and will return the
* processed amount of update processing to the client.
*/
void AutoConnectUpdateAct::_inProgress(size_t amount, size_t size) {
if (_ws) {
_amount = amount;
_binSize = size;
String payload = "#p," + String(_amount) + ':' + String(_binSize);
_ws->sendTXT(_wsClient, payload);
}
}
void AutoConnectUpdateAct::_progress(void) {
String reqOperation = "op";
String reqOperand;
String payload = String("");
int httpCode;
static const char reply_msg_op[] PROGMEM = "invalid operation";
static const char reply_msg_seq[] PROGMEM = "invalid sequence";
static const char reply_msg_inv[] PROGMEM = "invalid request";
static const char* content_type = "text/plain";
switch (_webServer->method()) {
case HTTP_POST:
if (_webServer->hasArg(reqOperation)) {
reqOperand = _webServer->arg(reqOperation);
if (reqOperand == String(UPDATE_NOTIFY_START)) {
if (_status == UPDATE_IDLE) {
httpCode = 200;
_status = UPDATE_START;
}
else {
payload = String(FPSTR(reply_msg_seq));
httpCode = 500;
}
}
else if (reqOperand == String(UPDATE_NOTIFY_REBOOT)) {
if (_status == UPDATE_SUCCESS) {
_status = UPDATE_RESET;
httpCode = 200;
}
else {
payload = String(FPSTR(reply_msg_seq));
httpCode = 500;
}
}
}
else {
payload = String(FPSTR(reply_msg_op));
httpCode = 500;
}
break;
/**
* An event handler of WebSocket which should be opened to monitor
* update progress. AutoConnectUpdate will send #s to start the update
* progress notification to the client when the WebSocket client
* connected. This has the meaning of ACK.
* @param client WS client number of WebSocketsServer
* @param event Event id
* @param payload Payload content
* @param length payload length
*/
void AutoConnectUpdateAct::_wsEvent(uint8_t client, WStype_t event, uint8_t* payload, size_t length) {
AC_DBG("WS client:%d event(%d)\n", client, event);
if (event == WStype_CONNECTED) {
_wsConnected = true;
_wsClient = client;
_ws->sendTXT(_wsClient, "#s");
case HTTP_GET:
switch (_status) {
case UPDATE_PROGRESS:
payload = String(UPDATE_NOTIFY_PROGRESS) + ',' + String(_amount) + ':' + String(_binSize);
httpCode = 200;
break;
case UPDATE_IDLE:
case UPDATE_SUCCESS:
case UPDATE_NOAVAIL:
case UPDATE_FAIL:
payload = String(UPDATE_NOTIFY_END);
httpCode = 200;
break;
default:
payload = String(FPSTR(reply_msg_seq));
httpCode = 500;
}
break;
default:
httpCode = 500;
payload = String(FPSTR(reply_msg_inv));
}
else if (event == WStype_DISCONNECTED)
_wsConnected = false;
_webServer->send(httpCode, content_type, payload);
}
#endif // !AUTOCONNECT_USE_UPDATE

@ -45,7 +45,7 @@ using HTTPUpdateClass = ESP8266HTTPUpdate;
#include <HTTPUpdate.h>
using HTTPUpdateClass = HTTPUpdate;
#endif
#include <WebSocketsServer.h>
// #include <WebSocketsServer.h>
// Quote the true AutoConnectUpdate class according to AUTOCONNECT_USE_UPDATE.
#define AutoConnectUpdate AutoConnectUpdateAct
#else // !AUTOCONNECT_USE_UPDATE!
@ -54,10 +54,10 @@ using HTTPUpdateClass = HTTPUpdate;
#include "AutoConnect.h"
// Support LED flashing only the board with built-in LED.
#ifdef LED_BUILTIN
#define UPDATE_SETLED(s) do {setLedPin(LED_BUILTIN, s);} while(0)
#if defined(BUILTIN_LED) || defined(LED_BUILTIN)
#define AC_SETLED(s) do {setLedPin(LED_BUILTIN, s);} while(0)
#else
#define UPDATE_SETLED(s) do {} while(0)
#define AC_SETLED(s) do {} while(0)
#endif
// Indicate an update process loop
@ -65,6 +65,7 @@ typedef enum AC_UPDATESTATUS {
UPDATE_RESET, /**< Update process ended, need to reset */
UPDATE_IDLE, /**< Update process has not started */
UPDATE_START, /**< Update process has been started */
UPDATE_PROGRESS, /**< Update process has been started */
UPDATE_SUCCESS, /**< Update successfully completed */
UPDATE_NOAVAIL, /**< No available update */
UPDATE_FAIL /**< Update fails */
@ -72,18 +73,20 @@ typedef enum AC_UPDATESTATUS {
class AutoConnectUpdateVoid {
public:
explicit AutoConnectUpdateVoid(const String& host = String(""), const uint16_t port = AUTOCONNECT_UPDATE_PORT, const String& uri = String("."), const int timeout = AUTOCONNECT_UPDATE_TIMEOUT) {
explicit AutoConnectUpdateVoid(const String& host = String(""), const uint16_t port = AUTOCONNECT_UPDATE_PORT, const String& uri = String("."), const int timeout = AUTOCONNECT_UPDATE_TIMEOUT, const uint8_t ledOn = LOW) {
AC_UNUSED(host);
AC_UNUSED(port);
AC_UNUSED(uri);
AC_UNUSED(timeout);
AC_UNUSED(ledOn);
}
AutoConnectUpdateVoid(AutoConnect& portal, const String& host = String(""), const uint16_t port = AUTOCONNECT_UPDATE_PORT, const String& uri = String("."), const int timeout = AUTOCONNECT_UPDATE_TIMEOUT) {
AutoConnectUpdateVoid(AutoConnect& portal, const String& host = String(""), const uint16_t port = AUTOCONNECT_UPDATE_PORT, const String& uri = String("."), const int timeout = AUTOCONNECT_UPDATE_TIMEOUT, const uint8_t ledOn = LOW) {
AC_UNUSED(portal);
AC_UNUSED(host);
AC_UNUSED(port);
AC_UNUSED(uri);
AC_UNUSED(timeout);
AC_UNUSED(ledOn);
}
virtual ~AutoConnectUpdateVoid() {}
virtual void attach(AutoConnect& portal) { AC_UNUSED(portal); }
@ -99,14 +102,14 @@ class AutoConnectUpdateVoid {
class AutoConnectUpdateAct : public AutoConnectUpdateVoid, public HTTPUpdateClass {
public:
explicit AutoConnectUpdateAct(const String& host = String(""), const uint16_t port = AUTOCONNECT_UPDATE_PORT, const String& uri = String("."), const int timeout = AUTOCONNECT_UPDATE_TIMEOUT)
: HTTPUpdateClass(timeout), host(host), port(port), uri(uri), _wsClient(0), _wsConnected(false), _amount(0), _binSize(0), _enable(false), _dialog(UPDATEDIALOG_LOADER), _status(UPDATE_IDLE), _binName(String()) {
UPDATE_SETLED(LOW); /**< LED blinking during the update that is the default. */
explicit AutoConnectUpdateAct(const String& host = String(""), const uint16_t port = AUTOCONNECT_UPDATE_PORT, const String& uri = String("."), const int timeout = AUTOCONNECT_UPDATE_TIMEOUT, const uint8_t ledOn = AUTOCONNECT_UPDATE_LEDON)
: HTTPUpdateClass(timeout), host(host), port(port), uri(uri), _amount(0), _binSize(0), _enable(false), _dialog(UPDATEDIALOG_LOADER), _status(UPDATE_IDLE), _binName(String()), _webServer(nullptr) {
AC_SETLED(ledOn); /**< LED blinking during the update that is the default. */
rebootOnUpdate(false); /**< Default reboot mode */
}
AutoConnectUpdateAct(AutoConnect& portal, const String& host = String(""), const uint16_t port = AUTOCONNECT_UPDATE_PORT, const String& uri = String("."), const int timeout = AUTOCONNECT_UPDATE_TIMEOUT)
: HTTPUpdateClass(timeout), host(host), port(port), uri(uri), _wsClient(0), _wsConnected(false), _amount(0), _binSize(0), _enable(false), _dialog(UPDATEDIALOG_LOADER), _status(UPDATE_IDLE), _binName(String()) {
UPDATE_SETLED(LOW);
AutoConnectUpdateAct(AutoConnect& portal, const String& host = String(""), const uint16_t port = AUTOCONNECT_UPDATE_PORT, const String& uri = String("."), const int timeout = AUTOCONNECT_UPDATE_TIMEOUT, const uint8_t ledOn = AUTOCONNECT_UPDATE_LEDON)
: HTTPUpdateClass(timeout), host(host), port(port), uri(uri), _amount(0), _binSize(0), _enable(false), _dialog(UPDATEDIALOG_LOADER), _status(UPDATE_IDLE), _binName(String()), _webServer(nullptr) {
AC_SETLED(ledOn);
rebootOnUpdate(false);
attach(portal);
}
@ -115,7 +118,7 @@ class AutoConnectUpdateAct : public AutoConnectUpdateVoid, public HTTPUpdateClas
void enable(void) override; /**< Enable the AutoConnectUpdateAct */
void disable(const bool activte = false) override; /**< Disable the AutoConnectUpdateAct */
void handleUpdate(void) override; /**< Behaves the update process */
bool isEnable(void) override { return _catalog ? _catalog->isMenu() : false; } /**< Returns current updater effectiveness */
bool isEnable(void) override { return _auxCatalog ? _auxCatalog->isMenu() : false; } /**< Returns current updater effectiveness */
AC_UPDATESTATUS_t status(void) override { return _status; } /**< reports the current update behavior status */
AC_UPDATESTATUS_t update(void) override; /**< behaves update */
@ -152,33 +155,38 @@ class AutoConnectUpdateAct : public AutoConnectUpdateVoid, public HTTPUpdateClas
String _onCatalog(AutoConnectAux& catalog, PageArgument& args);
String _onUpdate(AutoConnectAux& update, PageArgument& args);
String _onResult(AutoConnectAux& result, PageArgument& args);
void _wsEvent(uint8_t client, WStype_t event, uint8_t* payload, size_t length);
void _inProgress(size_t amount, size_t size); /**< UpdateClass::THandlerFunction_Progress */
std::unique_ptr<AutoConnectAux> _catalog; /**< A catalog page for internally generated update binaries */
std::unique_ptr<AutoConnectAux> _progress; /**< An update in-progress page */
std::unique_ptr<AutoConnectAux> _result; /**< A update result page */
std::unique_ptr<AutoConnectAux> _auxCatalog; /**< A catalog page for internally generated update binaries */
std::unique_ptr<AutoConnectAux> _auxProgress; /**< An update in-progress page */
std::unique_ptr<AutoConnectAux> _auxResult; /**< A update result page */
std::unique_ptr<WebSocketsServer> _ws; /**< Reports the update progress measure */
uint8_t _wsClient; /**< WebSocket client id */
bool _wsConnected; /**< WebSocket connection status */
size_t _amount; /**< Received amount bytes */
size_t _binSize; /**< Updater binary size */
size_t _amount; /**< Received amount bytes */
size_t _binSize; /**< Updater binary size */
private:
bool _enable; /**< Validation status of the Update class */
AC_UPDATEDIALOG_t _dialog; /**< The type of updating dialog displayed on the client */
AC_UPDATESTATUS_t _status; /**< Status of update processing during the cycle of receiving a request */
String _binName; /**< .bin name to update */
void _progress(void); /**< A Handler that returns progress status to the web client */
static const ACPage_t _auxCatalog PROGMEM;
bool _enable; /**< Validation status of the Update class */
AC_UPDATEDIALOG_t _dialog; /**< The type of updating dialog displayed on the client */
AC_UPDATESTATUS_t _status; /**< Status of update processing during the cycle of receiving a request */
String _binName; /**< .bin name to update */
WebServerClass* _webServer; /**< Hosted WebServer for XMLHttpRequest */
static const ACPage_t _pageCatalog PROGMEM;
static const ACElementProp_t _elmCatalog[] PROGMEM;
static const ACPage_t _auxProgress PROGMEM;
static const ACPage_t _pageProgress PROGMEM;
static const ACElementProp_t _elmProgress[] PROGMEM;
static const ACPage_t _auxResult PROGMEM;
static const ACPage_t _pageResult PROGMEM;
static const ACElementProp_t _elmResult[] PROGMEM;
#if defined(ARDUINO_ARCH_ESP8266)
friend class ESP8266WebServer;
#elif defined(ARDUINO_ARCH_ESP32)
friend class WebServer;
#endif
};
#endif // !AUTOCONNECT_USE_UPDATE

@ -21,7 +21,7 @@ const AutoConnectUpdateAct::ACElementProp_t AutoConnectUpdateAct::_elmCatalog[]
{ AC_Element, "c1", "</div>", nullptr },
{ AC_Submit, "update", "UPDATE", AUTOCONNECT_URI_UPDATE_ACT }
};
const AutoConnectUpdateAct::ACPage_t AutoConnectUpdateAct::_auxCatalog PROGMEM = {
const AutoConnectUpdateAct::ACPage_t AutoConnectUpdateAct::_pageCatalog PROGMEM = {
AUTOCONNECT_URI_UPDATE, "Update", false, AutoConnectUpdateAct::_elmCatalog
};
@ -36,27 +36,24 @@ const AutoConnectUpdateAct::ACElementProp_t AutoConnectUpdateAct::_elmProgress[]
{ AC_Element, "c3", "<div id=\"progress\">Updating...<span style=\"display:inline-block;vertical-align:middle;margin-left:7px\">", nullptr },
{ AC_Element, "progress_meter", "<meter min=\"0\" />", nullptr },
{ AC_Element, "progress_loader", "<div id=\"ld\" />", nullptr },
{ AC_Element, "c4", "</span></div></div>", nullptr },
{ AC_Element, "c4", "</span></div>", nullptr },
{ AC_Text, "status", nullptr, nullptr },
{ AC_Element, "c5", "<script type=\"text/javascript\">var ws,conn=!1;function rd(){location.href=\"" AUTOCONNECT_URI_UPDATE_RESULT "\"}function bar(){(ws=new WebSocket(\"", nullptr },
{ AC_Element, "wsurl", nullptr, nullptr },
{ AC_Element, "c6", "\")).onopen=function(){ws.onmessage=function(e){var t=e.data.split(\",\");\"#s\"==t[0]?(", nullptr },
{ AC_Element, "enable_loader", "document.getElementById(\"ld\").className=\"loader\",", nullptr },
{ AC_Element, "c7", "window.setTimeout(rd()," AUTOCONNECT_STRING_DEPLOY(AUTOCONNECT_UPDATE_DURATION) "),conn=!0):\"#e\"==t[0]?(ws.close(),conn=!1):\"#p\"==t[0]&&incr(t[1])}},ws.onclose=function(e){conn?setTimeout(function(){bar()},2e3):rd()},ws.onerror=function(e){console.log(\"WS err(\"+e.code+\")\"+e.reason),1==ws.readyState&&(document.getElementById(\"status\").textContent=\"WebSocket \"+e.type)}}", nullptr },
{ AC_Element, "inprogress_meter", "function incr(e){var t=e.split(\":\"),n=document.getElementById(\"progress\").getElementsByTagName(\"meter\");n[0].setAttribute(\"value\",t[0]),n[0].setAttribute(\"max\",t[1])}", nullptr },
{ AC_Element, "inprogress_loader", "function incr(pv){}", nullptr },
{ AC_Element, "c8", "window.onload=bar;</script>", nullptr }
{ AC_Element, "c5", "<script type=\"text/javascript\">var lap,cls;function rd(){clearInterval(lap),location.href=\"" AUTOCONNECT_URI_UPDATE_RESULT "\"}function bar(){var t=new FormData;t.append(\"op\",\"#s\");var e=new XMLHttpRequest;e.timeout=" AUTOCONNECT_STRING_DEPLOY(AUTOCONNECT_UPDATE_TIMEOUT) ",e.open(\"POST\",\"" AUTOCONNECT_URI_UPDATE_PROGRESS "\",!0),e.onreadystatechange=function(){4==e.readyState&&(200==e.status?(cls=!1,lap=setInterval(upd," AUTOCONNECT_STRING_DEPLOY(AUTOCONNECT_UPDATE_INTERVAL) ")):document.getElementById(\"status\").textContent=\"Could not start (\"+e.status+\"): \"+e.responseText)},e.send(t)}function upd(){if(!cls){var t=new XMLHttpRequest;t.onload=function(){var t=this.responseText.split(\",\");\"#s\"==t[0]?(window.setTimeout(rd()," AUTOCONNECT_STRING_DEPLOY(AUTOCONNECT_UPDATE_DURATION) ")", nullptr },
{ AC_Element, "enable_loader", ",document.getElementById(\"ld\").className=\"loader\"", nullptr },
{ AC_Element, "c6", "):\"#e\"==t[0]?(cls=!0,rd()):\"#p\"==t[0]&&incr(t[1])},t.onerror=function(){console.log(\"http err:%d %s\",t.status,t.responseText)},t.open(\"GET\",\"" AUTOCONNECT_URI_UPDATE_PROGRESS "\",!0),t.send()}}function incr(t){", nullptr },
{ AC_Element, "inprogress_meter", "var e=t.split(\":\"),n=document.getElementById(\"progress\").getElementsByTagName(\"meter\");n[0].setAttribute(\"value\",e[0]),n[0].setAttribute(\"max\",e[1])", nullptr },
{ AC_Element, "c7", "}window.onload=bar;</script>", nullptr }
};
const AutoConnectUpdateAct::ACPage_t AutoConnectUpdateAct::_auxProgress PROGMEM = {
const AutoConnectUpdateAct::ACPage_t AutoConnectUpdateAct::_pageProgress PROGMEM = {
AUTOCONNECT_URI_UPDATE_ACT, "Update", false, AutoConnectUpdateAct::_elmProgress
};
// Definition of the AUTOCONNECT_URI_UPDATE_RESULT page to notify update results
const AutoConnectUpdateAct::ACElementProp_t AutoConnectUpdateAct::_elmResult[] PROGMEM = {
{ AC_Text, "status", nullptr, nullptr },
{ AC_Element, "restart", "<script type=\"text/javascript\">setTimeout(\"location.href='" AUTOCONNECT_HOMEURI "'\",1e4);</script>", nullptr }
{ AC_Element, "restart", "<script type=\"text/javascript\">window.onload=function(){var e=new FormData;e.append(\"op\",\"#r\");var o=new XMLHttpRequest;o.timeout=" AUTOCONNECT_STRING_DEPLOY(AUTOCONNECT_UPDATE_TIMEOUT) ",o.onloadend=function(){setTimeout(\"location.href='" AUTOCONNECT_HOMEURI "'\"," AUTOCONNECT_STRING_DEPLOY(AUTOCONNECT_UPDATE_WAITFORREBOOT) ")},o.open(\"POST\",\"" AUTOCONNECT_URI_UPDATE_PROGRESS "\",!0),o.send(e)};</script>", nullptr }
};
const AutoConnectUpdateAct::ACPage_t AutoConnectUpdateAct::_auxResult PROGMEM = {
const AutoConnectUpdateAct::ACPage_t AutoConnectUpdateAct::_pageResult PROGMEM = {
AUTOCONNECT_URI_UPDATE_RESULT, "Update", false, AutoConnectUpdateAct::_elmResult
};

Loading…
Cancel
Save