From 5dcbf14a7753e396a67ec3658423c5505bbd04bc Mon Sep 17 00:00:00 2001 From: Hieromon Ikasamo Date: Wed, 28 Nov 2018 17:41:07 +0900 Subject: [PATCH] Supports AutoConnectAux --- keywords.txt | 2 + src/AutoConnect.cpp | 4 +- src/AutoConnectAux.cpp | 134 +++++++++++++++++++++--------- src/AutoConnectAux.h | 15 +++- src/AutoConnectElement.h | 3 + src/AutoConnectElementBasis.h | 38 ++++++++- src/AutoConnectElementBasisImpl.h | 30 ++++++- src/AutoConnectElementJson.h | 32 ++++++- src/AutoConnectElementJsonImpl.h | 30 ++++++- src/AutoConnectPage.cpp | 12 ++- 10 files changed, 246 insertions(+), 54 deletions(-) diff --git a/keywords.txt b/keywords.txt index 5b661b7..6560b48 100644 --- a/keywords.txt +++ b/keywords.txt @@ -9,6 +9,7 @@ AutoConnectButton KEYWORD1 AutoConnectCheckbox KEYWORD1 AutoConnectElement KEYWORD1 AutoConnectInput KEYWORD1 +AutoConnectRadio KEYWORD1 AutoConnectSelect KEYWORD1 AutoConnectSubmit KEYWORD1 AutoConnectText KEYWORD1 @@ -54,6 +55,7 @@ ACButton PREPROCESSOR ACCheckbox PREPROCESSOR ACElement PREPROCESSOR ACInput PREPROCESSOR +ACRadio PREPROCESSOR ACSelect PREPROCESSOR ACSubmit PREPROCESSOR ACText PREPROCESSOR diff --git a/src/AutoConnect.cpp b/src/AutoConnect.cpp index 8210fa2..a42c6ff 100644 --- a/src/AutoConnect.cpp +++ b/src/AutoConnect.cpp @@ -310,6 +310,7 @@ void AutoConnect::_startWebServer() { _webServer->onNotFound(std::bind(&AutoConnect::_handleNotFound, this)); // here, Prepare PageBuilders for captive portal _responsePage = new PageBuilder(); + _responsePage->chunked(PB_ByteStream); _responsePage->exitCanHandle(std::bind(&AutoConnect::_classifyHandle, this, std::placeholders::_1, std::placeholders::_2)); _responsePage->insert(*_webServer); @@ -538,10 +539,11 @@ String AutoConnect::_induceConnect(PageArgument& args) { // Read from EEPROM AutoConnectCredential credential(_apConfig.boundaryOffset); struct station_config entry; - AC_DBG("Load credential:%s\n", args.arg(AUTOCONNECT_PARAMID_CRED).c_str()); +// AC_DBG("Load credential:%s\n", args.arg(AUTOCONNECT_PARAMID_CRED).c_str()); credential.load(args.arg(AUTOCONNECT_PARAMID_CRED).c_str(), &entry); strncpy(reinterpret_cast(_credential.ssid), reinterpret_cast(entry.ssid), sizeof(_credential.ssid)); strncpy(reinterpret_cast(_credential.password), reinterpret_cast(entry.password), sizeof(_credential.password)); + AC_DBG("Credential loaded:%s %s\n", _credential.ssid, _credential.password); } else { // Credential had by the post parameter. diff --git a/src/AutoConnectAux.cpp b/src/AutoConnectAux.cpp index aa19702..c74399c 100644 --- a/src/AutoConnectAux.cpp +++ b/src/AutoConnectAux.cpp @@ -77,7 +77,7 @@ AutoConnectAux::~AutoConnectAux() { */ void AutoConnectAux::add(AutoConnectElement& addon) { _addonElm.push_back(addon); - AC_DBG("%s placed on %s\n", addon.name.length() ? addon.name.c_str() : "*nonamed*", uri()); + AC_DBG("%s placed on %s\n", addon.name.length() ? addon.name.c_str() : "*noname", uri()); } /** @@ -258,6 +258,10 @@ AutoConnectElement* AutoConnectAux::_createElement(const JsonObject& json) { AutoConnectInput* cert_elm = new AutoConnectInput; return reinterpret_cast(cert_elm); } + case AC_Radio: { + AutoConnectRadio* cert_elm = new AutoConnectRadio; + return reinterpret_cast(cert_elm); + } case AC_Select: { AutoConnectSelect* cert_elm = new AutoConnectSelect; return reinterpret_cast(cert_elm); @@ -274,59 +278,112 @@ AutoConnectElement* AutoConnectAux::_createElement(const JsonObject& json) { return elm; } +/** + * Constructs an AutoConnectAux instance by reading all the + * AutoConnectElements of the specified URI from the elements defined JSON. + * @param in AutoConnectAux element data which is described by JSON. + * @return true The element collection successfully loaded. + * @return false Invalid JSON data occurred. + */ +bool AutoConnectAux::load(const char* in) { +// DynamicJsonBuffer jsonBuffer(); + const size_t bufferSize = 2*JSON_ARRAY_SIZE(2) + 3*JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(9) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(3) + 9*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + 1080; + DynamicJsonBuffer jsonBuffer(bufferSize); + JsonObject& jb = jsonBuffer.parseObject(in); + return _load(jb); +} + +bool AutoConnectAux::load(const __FlashStringHelper* in) { +// DynamicJsonBuffer jsonBuffer(); + const size_t bufferSize = 2*JSON_ARRAY_SIZE(2) + 3*JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(9) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(3) + 9*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + 1080; + DynamicJsonBuffer jsonBuffer(bufferSize); + JsonObject& jb = jsonBuffer.parseObject(in); + return _load(jb); +} + +bool AutoConnectAux::load(Stream& in) { +// DynamicJsonBuffer jsonBuffer(); + const size_t bufferSize = 2*JSON_ARRAY_SIZE(2) + 3*JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(9) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(3) + 9*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + 1080; + DynamicJsonBuffer jsonBuffer(bufferSize); + JsonObject& jb = jsonBuffer.parseObject(in); + return _load(jb); +} + +bool AutoConnectAux::_load(JsonObject& jb) { + if (!jb.success()) + return false; + + _title = jb.get(F(AUTOCONNECT_JSON_KEY_TITLE)); + _uriStr = jb.get(F(AUTOCONNECT_JSON_KEY_URI)); + _uri = _uriStr.c_str(); + _menu = jb.get(F(AUTOCONNECT_JSON_KEY_MENU)); + (void)_loadElement(jb, "*"); + return true; +} + /** * Load element specified by the name parameter from the stream * described by JSON. Usually, the Stream is specified a storm file of * SD or SPIFFS. The Stream must be opened before invoking the function. * @param in Reference of the Stream which contains the parameter - * file described by JSON. - * @param name The element name to be loaded. + * data described by JSON. + * @param name The element name to be loaded. '*'specifies that all + * elements are to be loaded. * @return A reference of loaded AutoConnectElement instance. */ +AutoConnectElement& AutoConnectAux::loadElement(const char* in, const String name) { + DynamicJsonBuffer jsonBuffer; + JsonObject& jb = jsonBuffer.parseObject(in); + return _loadElement(jb, name); +} + +AutoConnectElement& AutoConnectAux::loadElement(const __FlashStringHelper* in, const String name) { + DynamicJsonBuffer jsonBuffer; + JsonObject& jb = jsonBuffer.parseObject(in); + return _loadElement(jb, name); +} + AutoConnectElement& AutoConnectAux::loadElement(Stream& in, const String name) { DynamicJsonBuffer jsonBuffer; JsonObject& jb = jsonBuffer.parseObject(in); + return _loadElement(jb, name); +} - if (!jb.success()) - return _nullElement(); +AutoConnectElement& AutoConnectAux::_loadElement(JsonObject& jb, const String name) { + AutoConnectElement* auxElm = nullptr; + bool wc = name == "*"; - JsonArray& aux = jb[AUTOCONNECT_JSON_KEY_AUX]; - if (!aux.success()) + if (!jb.success()) return _nullElement(); - for (JsonObject& page : aux) { - if (page["uri"].as() == String(uri())) { - JsonArray& element = page[AUTOCONNECT_JSON_KEY_ELEMENT]; - for (JsonObject& elm : element) { - if (name.equalsIgnoreCase(elm.get(F(AUTOCONNECT_JSON_KEY_NAME)))) { - // The specified element is defined in the JSON stream. - // Loads from JSON object. - const String inType = elm[AUTOCONNECT_JSON_KEY_TYPE].as(); - AutoConnectElement* auxElm = _getElement(name); - // The element is not created yet, create new one. - if (!auxElm) { - if ((auxElm = _createElement(elm))) { - AC_DBG("%s<%d> of %s created\n", name.c_str(), (int)(auxElm->typeOf()), uri()); - add(*auxElm); // Insert to AutoConnect - } - else { - AC_DBG("%s unknown element type\n", name.c_str()); - return _nullElement(); - } - } - if (auxElm->loadElement(elm)) { - AC_DBG("%s<%d> of %s loaded\n", name.c_str(), (int)auxElm->typeOf(), uri()); - } - else { - // Element type mismatch - AC_DBG("Type of %s element mismatched\n", name.c_str()); - return _nullElement(); - } + JsonArray& elements = jb[AUTOCONNECT_JSON_KEY_ELEMENT]; + for (JsonObject& element : elements) { + String elmName = element.get(F(AUTOCONNECT_JSON_KEY_NAME)); + if (wc || name.equalsIgnoreCase(elmName)) { + // The specified element is defined in the JSON stream. + // Loads from JSON object. + auxElm = _getElement(elmName); + // The element is not created yet, create new one. + if (!auxElm) { + if ((auxElm = _createElement(element))) { + AC_DBG("%s<%d> of %s created\n", elmName.c_str(), (int)(auxElm->typeOf()), uri()); + add(*auxElm); // Insert to AutoConnect } + else { + AC_DBG("%s unknown element type\n", elmName.c_str()); + continue; + } + } + if (auxElm->loadElement(element)) + AC_DBG("%s<%d> of %s loaded\n", auxElm->name.c_str(), (int)auxElm->typeOf(), uri()); + else { + // Element type mismatch + AC_DBG("Type of %s element mismatched\n", elmName.c_str()); + continue; } } } - return _nullElement(); + return auxElm ? *auxElm : _nullElement(); } /** @@ -342,12 +399,12 @@ size_t AutoConnectAux::saveElement(Stream& out, const AutoConnectElement& elemen if (!jb.success()) return 0; - JsonArray& aux = jb[AUTOCONNECT_JSON_KEY_AUX]; + JsonArray& aux = jb["aux"]; if (!aux.success()) return 0; for (JsonObject& page : aux) { - if (page["uri"].as() == String(uri())) { + if (page["aux"].as() == String(uri())) { JsonArray& element_j = page[AUTOCONNECT_JSON_KEY_ELEMENT]; for (JsonObject& elm : element_j) { if (elm[AUTOCONNECT_JSON_KEY_NAME].as() == element.name) { @@ -387,6 +444,7 @@ const ACElement_t AutoConnectAux::_asElementType(const String type) { { AUTOCONNECT_JSON_TYPE_ACCHECKBOX, AC_Checkbox }, { AUTOCONNECT_JSON_TYPE_ACELEMENT, AC_Element }, { AUTOCONNECT_JSON_TYPE_ACINPUT, AC_Input }, + { AUTOCONNECT_JSON_TYPE_ACRADIO, AC_Radio }, { AUTOCONNECT_JSON_TYPE_ACSELECT, AC_Select }, { AUTOCONNECT_JSON_TYPE_ACSUBMIT, AC_Submit }, { AUTOCONNECT_JSON_TYPE_ACTEXT, AC_Text } diff --git a/src/AutoConnectAux.h b/src/AutoConnectAux.h index d49c8f7..e935688 100644 --- a/src/AutoConnectAux.h +++ b/src/AutoConnectAux.h @@ -10,11 +10,14 @@ #ifndef _AUTOCONNECTAUX_H_ #define _AUTOCONNECTAUX_H_ +#include "AutoConnectDefs.h" #include #include #include +#ifdef AUTOCONNECT_USE_JSON +#include +#endif #include -#include "AutoConnectDefs.h" #include "AutoConnectElement.h" class AutoConnect; @@ -57,7 +60,12 @@ class AutoConnectAux : public PageBuilder { void on(const AuxHandlerFunctionT handler, const AutoConnectExitOrder_t order = AC_EXIT_AHEAD) { _handler = handler; _order = order; } /**< Set user handler */ #ifdef AUTOCONNECT_USE_JSON - AutoConnectElement& loadElement(Stream& in, const String name); + bool load(const char* in); + bool load(const __FlashStringHelper* in); + bool load(Stream& in); + AutoConnectElement& loadElement(const char* in, const String name = "*"); + AutoConnectElement& loadElement(const __FlashStringHelper* in, const String name = "*"); + AutoConnectElement& loadElement(Stream& in, const String name = "*"); size_t saveElement(Stream& out, const AutoConnectElement& element); #endif @@ -70,6 +78,8 @@ class AutoConnectAux : public PageBuilder { const String _injectMenu(PageArgument& args); #ifdef AUTOCONNECT_USE_JSON + bool _load(JsonObject& in); + AutoConnectElement& _loadElement(JsonObject& in, const String name); AutoConnectElement* _createElement(const JsonObject& json); AutoConnectElement* _getElement(const String name); static const ACElement_t _asElementType(const String type); @@ -78,6 +88,7 @@ class AutoConnectAux : public PageBuilder { String _title; /**< A title of the page */ bool _menu; /**< Switch for menu displaying */ + String _uriStr; /**< uri as String */ AutoConnectElementVT _addonElm; /**< A vector set of AutoConnectElements placed on this auxiliary page */ std::unique_ptr _next; /**< Auxiliary pages chain list */ std::unique_ptr _ac; /**< Hosted AutoConnect instance */ diff --git a/src/AutoConnectElement.h b/src/AutoConnectElement.h index ef88467..b1e4cb7 100644 --- a/src/AutoConnectElement.h +++ b/src/AutoConnectElement.h @@ -20,6 +20,7 @@ using AutoConnectElement = AutoConnectElementJson; using AutoConnectButton = AutoConnectButtonJson; using AutoConnectCheckbox = AutoConnectCheckboxJson; using AutoConnectInput = AutoConnectInputJson; +using AutoConnectRadio = AutoConnectRadioJson; using AutoConnectSelect = AutoConnectSelectJson; using AutoConnectSubmit = AutoConnectSubmitJson; using AutoConnectText = AutoConnectTextJson; @@ -28,6 +29,7 @@ using AutoConnectElement = AutoConnectElementBasis; using AutoConnectButton = AutoConnectButtonBasis; using AutoConnectCheckbox = AutoConnectCheckboxBasis; using AutoConnectInput = AutoConnectInputBasis; +using AutoConnectRadio = AutoConnectRadioBasis; using AutoConnectSelect = AutoConnectSelectBasis; using AutoConnectSubmit = AutoConnectSubmitBasis; using AutoConnectText = AutoConnectTextBasis; @@ -42,6 +44,7 @@ using AutoConnectText = AutoConnectTextBasis; #define ACButton(n, ...) AutoConnectButton n(#n, ##__VA_ARGS__) #define ACCheckbox(n, ...) AutoConnectCheckbox n(#n, ##__VA_ARGS__) #define ACInput(n, ...) AutoConnectInput n(#n, ##__VA_ARGS__) +#define ACRadio(n, ...) AutoConnectRadio n(#n, ##__VA_ARGS__) #define ACSelect(n, ...) AutoConnectSelect n(#n, ##__VA_ARGS__) #define ACSubmit(n, ...) AutoConnectSubmit n(#n, ##__VA_ARGS__) #define ACText(n, ...) AutoConnectText n(#n, ##__VA_ARGS__) diff --git a/src/AutoConnectElementBasis.h b/src/AutoConnectElementBasis.h index bac2bf6..37d66ed 100644 --- a/src/AutoConnectElementBasis.h +++ b/src/AutoConnectElementBasis.h @@ -18,12 +18,18 @@ typedef enum { AC_Checkbox, AC_Element, AC_Input, + AC_Radio, AC_Select, AC_Submit, AC_Text, AC_Unknown } ACElement_t; /**< AutoConnectElement class type */ +typedef enum { + AC_Horizontal, + AC_Vertical +} ACArrange_t; /**< The element arrange order */ + /** * AutoConnectAux element base. * Placed a raw text that can be added by user sketch. @@ -42,7 +48,7 @@ class AutoConnectElementBasis { String name; /**< Element name */ String value; /**< Element value */ -protected: + protected: ACElement_t _type; /**< Element type identifier */ }; @@ -103,6 +109,32 @@ class AutoConnectInputBasis : virtual public AutoConnectElementBasis { String label; /**< A label for a subsequent input box */ }; +/** + * Radio-button arrangement class, a part of AutoConnectAux element. + * Place a group of radio-button items and selectable mark checked. + * @param name Radio-button name string. + * @param options Array of value collection. + * @param label A label string that follows radio-buttons group. + * @param checked Index of check marked item. + */ +class AutoConnectRadioBasis : virtual public AutoConnectElementBasis { + public: + explicit AutoConnectRadioBasis(const char* name = "", std::vector values = {}, const char* label = "", const ACArrange_t order = AC_Vertical, const uint8_t checked = 0) : AutoConnectElementBasis(name, ""), label(label), order(order), checked(checked), _values(values) { + _type = AC_Radio; + } + virtual ~AutoConnectRadioBasis() {} + const String toHTML(void) const; + void add(const String value) { _values.push_back(value); } + void empty(void) { _values.clear(); } + + String label; /**< A label for a subsequent radio buttons */ + ACArrange_t order; /**< layout order */ + uint8_t checked; /**< Index of check marked item */ + + protected: + std::vector _values; /**< Items in a group */ +}; + /** * Selection-box arrangement class, A part of AutoConnectAux element. * Place a optionally labeled Selection-box that can be added by user sketch. @@ -113,12 +145,12 @@ class AutoConnectInputBasis : virtual public AutoConnectElementBasis { */ class AutoConnectSelectBasis : virtual public AutoConnectElementBasis { public: - explicit AutoConnectSelectBasis(const char* name = "", std::vector options = {}, const char* label = "") : AutoConnectElementBasis(name, ""), label(String(label)), _options(options) { + explicit AutoConnectSelectBasis(const char* name = "", std::vector options = {}, const char* label = "") : AutoConnectElementBasis(name, ""), label(String(label)), _options(options) { _type = AC_Select; } virtual ~AutoConnectSelectBasis() {} const String toHTML(void) const; - void option(const String value) { _options.push_back(value); } + void add(const String option) { _options.push_back(option); } void empty(void) { _options.clear(); } String label; /**< A label for a subsequent input box */ diff --git a/src/AutoConnectElementBasisImpl.h b/src/AutoConnectElementBasisImpl.h index 6292fb5..1a3d171 100644 --- a/src/AutoConnectElementBasisImpl.h +++ b/src/AutoConnectElementBasisImpl.h @@ -62,6 +62,30 @@ const String AutoConnectInputBasis::toHTML(void) const { return html; } +/** +* Generate an HTML element with an