From f0dde870646eac8c221cd98abed7c4079907172c Mon Sep 17 00:00:00 2001 From: Hieromon Ikasamo Date: Wed, 19 Jun 2019 14:53:08 +0900 Subject: [PATCH] AutoConnectUpdate-rc1 --- src/AutoConnectUpdate.cpp | 98 ++++++++++++++++++++++--------------- src/AutoConnectUpdate.h | 23 +++++---- src/AutoConnectUpdatePage.h | 18 +++---- 3 files changed, 78 insertions(+), 61 deletions(-) diff --git a/src/AutoConnectUpdate.cpp b/src/AutoConnectUpdate.cpp index 8bd67b7..18e74b7 100644 --- a/src/AutoConnectUpdate.cpp +++ b/src/AutoConnectUpdate.cpp @@ -89,14 +89,20 @@ namespace AutoConnectUtil { AC_HAS_FUNC(onProgress); template -typename std::enable_if::value, AutoConnectUpdateAct::AC_UPDATEDIALOG_t>::type onProgress(T& updater, std::function fn) { +typename std::enable_if::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 std::enable_if::value, AutoConnectUpdateAct::AC_UPDATEDIALOG_t>::type onProgress(T& updater, std::function fn) { +typename std::enable_if::value, AutoConnectUpdateAct::AC_UPDATEDIALOG_t>::type onProgress(T& updater, UpdateVariedClass::THandlerFunction_Progress fn) { (void)(updater); (void)(fn); return AutoConnectUpdateAct::UPDATEDIALOG_LOADER; @@ -111,7 +117,6 @@ AutoConnectUpdateAct::~AutoConnectUpdateAct() { _catalog.reset(nullptr); _progress.reset(nullptr); _result.reset(nullptr); - _WiFiClient.reset(nullptr); _ws.reset(nullptr); } @@ -136,7 +141,7 @@ void AutoConnectUpdateAct::attach(AutoConnect& portal) { updatePage = new AutoConnectAux(String(FPSTR(_auxResult.uri)), String(FPSTR(_auxResult.title)), _auxResult.menu); _buildAux(updatePage, &_auxResult, lengthOf(_elmResult)); _result.reset(updatePage); - _result->chunk = PB_ByteStream; + _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); @@ -159,7 +164,6 @@ void AutoConnectUpdateAct::attach(AutoConnect& portal) { // of the UpdateClass. AutoConnectElement* loader = _progress->getElement(String(F("loader"))); AutoConnectElement* enable_loader = _progress->getElement(String(F("enable_loader"))); - AutoConnectElement* enable_loader_post = _progress->getElement(String(F("enable_loader_post"))); 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"))); @@ -172,7 +176,6 @@ void AutoConnectUpdateAct::attach(AutoConnect& portal) { case UPDATEDIALOG_METER: loader->enable = false; enable_loader->enable =false; - enable_loader_post->enable =false; progress_loader->enable =false; inprogress_loader->enable = false; break; @@ -183,11 +186,10 @@ void AutoConnectUpdateAct::attach(AutoConnect& portal) { * Detach the update item from the current AutoConnect menu. * AutoConnectUpdateAct still active. */ -void AutoConnectUpdateAct::disable(void) { +void AutoConnectUpdateAct::disable(const bool activate) { + _enable = activate; if (_catalog) { _catalog->menu(false); - if (_WiFiClient) - _WiFiClient.reset(nullptr); AC_DBG("AutoConnectUpdate disabled\n"); } } @@ -197,23 +199,19 @@ void AutoConnectUpdateAct::disable(void) { * function into the menu. */ void AutoConnectUpdateAct::enable(void) { + _enable = true; + _status = UPDATE_IDLE; if (_catalog) { - _catalog->menu(true); - _status = UPDATE_IDLE; - _period = 0; + _catalog->menu(WiFi.status() == WL_CONNECTED); AC_DBG("AutoConnectUpdate enabled\n"); } } void AutoConnectUpdateAct::handleUpdate(void) { - // Purge WiFiClient instances that have exceeded their retention - // period to avoid running out of memory. - if (_WiFiClient) { - if (millis() - _period > AUTOCONNECT_UPDATE_DURATION) - if (_status != UPDATE_START) { - _WiFiClient.reset(nullptr); - AC_DBG("Purged WifiClient due to duration expired.\n"); - } + // Activate the update menu conditional with WiFi connected. + if (!isEnable() && _enable) { + if (WiFi.status() == WL_CONNECTED) + enable(); } if (isEnable()) { @@ -222,6 +220,7 @@ 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) { @@ -229,6 +228,7 @@ void AutoConnectUpdateAct::handleUpdate(void) { break; } _ws->loop(); // Crawl the connection request. + yield(); } // Launch the update if (_wsConnected) @@ -244,7 +244,7 @@ void AutoConnectUpdateAct::handleUpdate(void) { // If WiFi is not connected, disables the update menu. // However, the AutoConnectUpdateAct class stills active. else - disable(); + disable(_enable); } } @@ -258,11 +258,12 @@ AC_UPDATESTATUS_t AutoConnectUpdateAct::update(void) { String uriBin = uri + '/' + _binName; if (_binName.length()) { AC_DBG("%s:%d/%s update in progress...", host.c_str(), port, uriBin.c_str()); - if (!_WiFiClient) { - _WiFiClient.reset(new WiFiClient); - _period = millis(); - } - t_httpUpdate_return ret = HTTPUpdateClass::update(*_WiFiClient, host, port, uriBin); +#if defined(ARDUINO_ARCH_ESP8266) + t_httpUpdate_return ret = HTTPUpdateClass::update(host, port, uriBin); +#elif defined(ARDUINO_ARCH_ESP32) + WiFiClient wifiClient; + t_httpUpdate_return ret = HTTPUpdateClass::update(wifiClient, host, port, uriBin); +#endif switch (ret) { case HTTP_UPDATE_FAILED: _status = UPDATE_FAIL; @@ -278,7 +279,6 @@ AC_UPDATESTATUS_t AutoConnectUpdateAct::update(void) { AC_DBG_DUMB(" completed\n"); break; } - _WiFiClient.reset(nullptr); // Request the client to close the WebSocket. _ws->sendTXT(_wsClient, "#e"); } @@ -349,19 +349,14 @@ String AutoConnectUpdateAct::_onCatalog(AutoConnectAux& catalog, PageArgument& a AC_UNUSED(args); HTTPClient httpClient; - // Reallocate WiFiClient if it is not existence. - if (!_WiFiClient) { - _WiFiClient.reset(new WiFiClient); - _period = millis(); - } - + // Reallocate available firmwares list. + _binName = String(""); AutoConnectText& caption = catalog.getElement(String(F("caption"))); AutoConnectRadio& firmwares = catalog.getElement(String(F("firmwares"))); AutoConnectSubmit& submit = catalog.getElement(String(F("update"))); firmwares.empty(); firmwares.tags.clear(); submit.enable = false; - _binName = String(""); String qs = String(F(AUTOCONNECT_UPDATE_CATALOG)) + '?' + String(F("op=list&path=")) + uri; AC_DBG("Query %s:%d%s\n", host.c_str(), port, qs.c_str()); @@ -369,7 +364,7 @@ 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(*_WiFiClient.get(), host, port, qs, false)) { + if (httpClient.begin(host, port, qs)) { int responseCode = httpClient.GET(); if (responseCode == HTTP_CODE_OK) { @@ -378,7 +373,7 @@ String AutoConnectUpdateAct::_onCatalog(AutoConnectAux& catalog, PageArgument& a char beginOfList[] = "["; char endOfEntry[] = ","; char endOfList[] = "]"; - Stream& responseBody = httpClient.getStream(); + WiFiClient& responseBody = httpClient.getStream(); // Read partially and repeatedly the responded http stream that is // including the JSON array to reduce the buffer size for parsing @@ -491,9 +486,15 @@ String AutoConnectUpdateAct::_onResult(AutoConnectAux& result, PageArgument& arg bool restart = false; if (_ws) { - _ws->close(); - while (_wsConnected) - _ws->loop(); + _ws->loop(); + while (_wsConnected) { + IPAddress ipAny; + if (_ws->remoteIP(_wsClient) == ipAny) + _wsConnected = false; + else + _ws->close(); + yield(); + } _ws.reset(nullptr); } @@ -519,9 +520,18 @@ String AutoConnectUpdateAct::_onResult(AutoConnectAux& result, PageArgument& arg result.getElement(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. + */ void AutoConnectUpdateAct::_inProgress(size_t amount, size_t size) { if (_ws) { _amount = amount; @@ -531,6 +541,16 @@ void AutoConnectUpdateAct::_inProgress(size_t amount, size_t size) { } } +/** + * 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) { diff --git a/src/AutoConnectUpdate.h b/src/AutoConnectUpdate.h index bd71e3c..8ddae2e 100644 --- a/src/AutoConnectUpdate.h +++ b/src/AutoConnectUpdate.h @@ -88,7 +88,7 @@ class AutoConnectUpdateVoid { virtual ~AutoConnectUpdateVoid() {} virtual void attach(AutoConnect& portal) { AC_UNUSED(portal); } virtual void enable(void) {} - virtual void disable(void) {} + virtual void disable(const bool activate = false) { AC_UNUSED(activate); } virtual void handleUpdate(void) {} virtual bool isEnable(void) { return false; } virtual AC_UPDATESTATUS_t status(void) { return UPDATE_IDLE; } @@ -100,12 +100,12 @@ 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), _status(UPDATE_IDLE), _binName(String()), _period(0) { + : 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. */ 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), _status(UPDATE_IDLE), _binName(String()), _period(0) { + : 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); rebootOnUpdate(false); attach(portal); @@ -113,7 +113,7 @@ class AutoConnectUpdateAct : public AutoConnectUpdateVoid, public HTTPUpdateClas ~AutoConnectUpdateAct(); void attach(AutoConnect& portal) override; /**< Attach the update class to AutoConnect */ void enable(void) override; /**< Enable the AutoConnectUpdateAct */ - void disable(void) override; /**< Disable 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 */ AC_UPDATESTATUS_t status(void) override { return _status; } /**< reports the current update behavior status */ @@ -123,10 +123,10 @@ class AutoConnectUpdateAct : public AutoConnectUpdateVoid, public HTTPUpdateClas uint16_t port; /**< Port number of the update server */ String uri; /**< The path on the update server that contains the sketch binary to be updated */ - // + // Indicate the type of progress dialog typedef enum { - UPDATEDIALOG_LOADER, - UPDATEDIALOG_METER + UPDATEDIALOG_LOADER, /**< Cyclic loader icon */ + UPDATEDIALOG_METER /**< Progress meter */ } AC_UPDATEDIALOG_t; protected: @@ -141,7 +141,7 @@ class AutoConnectUpdateAct : public AutoConnectUpdateVoid, public HTTPUpdateClas // Attributes to treat included update pages as AutoConnectAux. typedef struct { const char* uri; /**< URI for the page */ - const char* title; /**< Menut title of update page */ + const char* title; /**< Menu title of update page */ const bool menu; /**< Whether to display in menu */ const ACElementProp_t* element; } ACPage_t; @@ -153,7 +153,7 @@ class AutoConnectUpdateAct : public AutoConnectUpdateVoid, public HTTPUpdateClas 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); + void _inProgress(size_t amount, size_t size); /**< UpdateClass::THandlerFunction_Progress */ std::unique_ptr _catalog; /**< A catalog page for internally generated update binaries */ std::unique_ptr _progress; /**< An update in-progress page */ @@ -162,15 +162,14 @@ class AutoConnectUpdateAct : public AutoConnectUpdateVoid, public HTTPUpdateClas std::unique_ptr _ws; /**< Reports the update progress measure */ uint8_t _wsClient; /**< WebSocket client id */ bool _wsConnected; /**< WebSocket connection status */ - size_t _amount; /**< Received amound bytes */ + 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 */ - unsigned long _period; /**< Duration of WiFiClient holding for the connection with the update server */ - std::unique_ptr _WiFiClient; /**< Provide to HTTPUpdate class */ static const ACPage_t _auxCatalog PROGMEM; static const ACElementProp_t _elmCatalog[] PROGMEM; diff --git a/src/AutoConnectUpdatePage.h b/src/AutoConnectUpdatePage.h index 0101e8b..d4aff16 100644 --- a/src/AutoConnectUpdatePage.h +++ b/src/AutoConnectUpdatePage.h @@ -14,7 +14,7 @@ // Define the AUTOCONNECT_URI_UPDATE page to select the sketch binary // for update and order update execution. const AutoConnectUpdateAct::ACElementProp_t AutoConnectUpdateAct::_elmCatalog[] PROGMEM = { - { AC_Element, "binSty", "", nullptr }, + { AC_Element, "binSty", "", nullptr }, { AC_Text, "caption", nullptr, nullptr }, { AC_Element, "c1", "
", nullptr }, { AC_Radio, "firmwares", nullptr, nullptr }, @@ -38,16 +38,14 @@ const AutoConnectUpdateAct::ACElementProp_t AutoConnectUpdateAct::_elmProgress[] { AC_Element, "progress_loader", "
", nullptr }, { AC_Element, "c4", "
", nullptr }, { AC_Text, "status", nullptr, nullptr }, - { AC_Element, "c5", "", nullptr } + { AC_Element, "c8", "window.onload=bar;", nullptr } }; const AutoConnectUpdateAct::ACPage_t AutoConnectUpdateAct::_auxProgress PROGMEM = { AUTOCONNECT_URI_UPDATE_ACT, "Update", false, AutoConnectUpdateAct::_elmProgress @@ -56,7 +54,7 @@ const AutoConnectUpdateAct::ACPage_t AutoConnectUpdateAct::_auxProgress PROGMEM // 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", "", nullptr } + { AC_Element, "restart", "", nullptr } }; const AutoConnectUpdateAct::ACPage_t AutoConnectUpdateAct::_auxResult PROGMEM = { AUTOCONNECT_URI_UPDATE_RESULT, "Update", false, AutoConnectUpdateAct::_elmResult