diff --git a/examples/FileUpload/FileUpload.ino b/examples/FileUpload/FileUpload.ino index 6bc4c48..eef78ca 100644 --- a/examples/FileUpload/FileUpload.ino +++ b/examples/FileUpload/FileUpload.ino @@ -115,10 +115,10 @@ String postUpload(AutoConnectAux& aux, PageArgument& args) { String content; // Explicitly cast to the desired element to correctly extract // the element using the operator []. - AutoConnectFile& filename = (AutoConnectFile&)auxUpload["filename"]; - AutoConnectText& aux_filename = (AutoConnectText&)aux["filename"]; - AutoConnectText& aux_size = (AutoConnectText&)aux["size"]; - AutoConnectText& aux_contentType = (AutoConnectText&)aux["content_type"]; + AutoConnectFile& filename = auxUpload["filename"].as(); + AutoConnectText& aux_filename = aux["filename"].as(); + AutoConnectText& aux_size = aux["size"].as(); + AutoConnectText& aux_contentType = aux["content_type"].as(); // Assignment operator can be used for the element attribute. aux_filename.value = filename.value; aux_size.value = String(filename.size); diff --git a/examples/Simple/Simple.ino b/examples/Simple/Simple.ino index 489fad5..ecbe93e 100644 --- a/examples/Simple/Simple.ino +++ b/examples/Simple/Simple.ino @@ -158,7 +158,7 @@ void setup() { Timezone.load(AUX_TIMEZONE); // Retrieve the select element that holds the time zone code and // register the zone mnemonic in advance. - AutoConnectSelect& tz = Timezone.getElement("timezone"); + AutoConnectSelect& tz = Timezone["timezone"].as(); for (uint8_t n = 0; n < sizeof(TZ) / sizeof(Timezone_t); n++) { tz.add(String(TZ[n].zone)); } diff --git a/examples/mqttRSSI/mqttRSSI.ino b/examples/mqttRSSI/mqttRSSI.ino index 3052261..15d69b4 100644 --- a/examples/mqttRSSI/mqttRSSI.ino +++ b/examples/mqttRSSI/mqttRSSI.ino @@ -256,42 +256,38 @@ String loadParams(AutoConnectAux& aux, PageArgument& args) { String saveParams(AutoConnectAux& aux, PageArgument& args) { // The 'where()' function returns the AutoConnectAux that caused // the transition to this page. - AutoConnectAux* mqtt_setting = portal.where(); + AutoConnectAux& mqtt_setting = portal.where(); - AutoConnectInput& mqttserver = mqtt_setting->getElement("mqttserver"); + AutoConnectInput& mqttserver = mqtt_setting["mqttserver"].as(); serverName = mqttserver.value; serverName.trim(); - AutoConnectInput& channelid = mqtt_setting->getElement("channelid"); - channelId = channelid.value; + channelId = mqtt_setting["channelid"].value; channelId.trim(); - AutoConnectInput& userkey = mqtt_setting->getElement("userkey"); - userKey = userkey.value; + userKey = mqtt_setting["userkey"].value; userKey.trim(); - AutoConnectInput& apikey = mqtt_setting->getElement("apikey"); - apiKey = apikey.value; + apiKey = mqtt_setting["apikey"].value; apiKey.trim(); - AutoConnectRadio& period = mqtt_setting->getElement("period"); + AutoConnectRadio& period = mqtt_setting["period"].as(); updateInterval = period.value().substring(0, 2).toInt() * 1000; - bool uniqueid = mqtt_setting->getElement("uniqueid").checked; + bool uniqueid = mqtt_setting["uniqueid"].as().checked; - AutoConnectInput& hostname = mqtt_setting->getElement("hostname"); - hostName = hostname.value; + hostName = mqtt_setting["hostname"].value; hostName.trim(); // The entered value is owned by AutoConnectAux of /mqtt_setting. // To retrieve the elements of /mqtt_setting, it is necessary to get // the AutoConnectAux object of /mqtt_setting. File param = SPIFFS.open(PARAM_FILE, "w"); - mqtt_setting->saveElement(param, { "mqttserver", "channelid", "userkey", "apikey", "uniqueid", "hostname" }); + mqtt_setting.saveElement(param, { "mqttserver", "channelid", "userkey", "apikey", "uniqueid", "hostname" }); param.close(); // Echo back saved parameters to AutoConnectAux page. - AutoConnectText& echo = aux.getElement("parameters"); + AutoConnectText& echo = aux["parameters"].as(); echo.value = "Server: " + serverName; echo.value += mqttserver.isValid() ? String(" (OK)") : String(" (ERR)"); echo.value += "
Channel ID: " + channelId + "
"; @@ -354,9 +350,9 @@ void setup() { SPIFFS.begin(); if (portal.load(FPSTR(AUX_mqtt_setting))) { - AutoConnectAux* mqtt_setting = portal.aux(AUX_SETTING_URI); - AutoConnectCheckbox& uniqueidElm = mqtt_setting->getElement("uniqueid"); - AutoConnectInput& hostnameElm = mqtt_setting->getElement("hostname"); + AutoConnectAux& mqtt_setting = *portal.aux(AUX_SETTING_URI); + AutoConnectCheckbox& uniqueidElm = mqtt_setting["uniqueid"].as(); + AutoConnectInput& hostnameElm = mqtt_setting["hostname"].as(); if (uniqueidElm.checked) { config.apid = String("ESP") + "-" + String(GET_CHIPID(), HEX); Serial.println("apid set to " + config.apid); diff --git a/examples/mqttRSSI_FS/mqttRSSI_FS.ino b/examples/mqttRSSI_FS/mqttRSSI_FS.ino index 9a3ac21..59e8bca 100644 --- a/examples/mqttRSSI_FS/mqttRSSI_FS.ino +++ b/examples/mqttRSSI_FS/mqttRSSI_FS.ino @@ -149,7 +149,7 @@ String saveParams(AutoConnectAux& aux, PageArgument& args) { param.close(); // Echo back saved parameters to AutoConnectAux page. - AutoConnectText& echo = aux.getElement("parameters"); + AutoConnectText& echo = aux["parameters"].as(); echo.value = "Server: " + serverName + "
"; echo.value += "Channel ID: " + channelId + "
"; echo.value += "User Key: " + userKey + "
"; @@ -231,9 +231,10 @@ void setup() { AutoConnectAux* setting = portal.aux(AUX_MQTTSETTING); if (setting) { PageArgument args; - loadParams(*setting, args); - AutoConnectCheckbox& uniqueidElm = setting->getElement("uniqueid"); - AutoConnectInput& hostnameElm = setting->getElement("hostname"); + AutoConnectAux& mqtt_setting = *setting; + loadParams(mqtt_setting, args); + AutoConnectCheckbox& uniqueidElm = mqtt_setting["uniqueid"].as(); + AutoConnectInput& hostnameElm = mqtt_setting["hostname"].as(); if (uniqueidElm.checked) { config.apid = String("ESP") + "-" + String(GET_CHIPID(), HEX); Serial.println("apid set to " + config.apid); diff --git a/src/AutoConnect.h b/src/AutoConnect.h index 2c697e6..8519342 100644 --- a/src/AutoConnect.h +++ b/src/AutoConnect.h @@ -185,7 +185,7 @@ class AutoConnect { void join(AutoConnectAux& aux); void join(AutoConnectAuxVT auxVector); bool on(const String& uri, const AuxHandlerFunctionT handler, AutoConnectExitOrder_t order = AC_EXIT_AHEAD); - AutoConnectAux* where(void) const { return aux(_auxUri); } + AutoConnectAux& where(void) const { return *aux(_auxUri); } /** For AutoConnectAux described in JSON */ #ifdef AUTOCONNECT_USE_JSON @@ -220,6 +220,8 @@ class AutoConnect { void _purgePages(void); virtual PageElement* _setupPage(String uri); #ifdef AUTOCONNECT_USE_JSON + template + bool _parseJson(T in); bool _load(JsonVariant& aux); #endif // !AUTOCONNECT_USE_JSON diff --git a/src/AutoConnectAux.cpp b/src/AutoConnectAux.cpp index c3328cf..b00b997 100644 --- a/src/AutoConnectAux.cpp +++ b/src/AutoConnectAux.cpp @@ -9,8 +9,8 @@ #include #include "AutoConnect.h" #include "AutoConnectAux.h" +#include "AutoConnectAuxImpl.h" #include "AutoConnectUploadImpl.h" -#include "AutoConnectElement.h" #include "AutoConnectElementBasisImpl.h" #ifdef AUTOCONNECT_USE_JSON #include "AutoConnectElementJsonImpl.h" @@ -466,309 +466,7 @@ void AutoConnectAux::_storeElements(WebServerClass* webServer) { } } -#ifndef AUTOCONNECT_USE_JSON - -/** - * Get AutoConnectElementBasis element. - * @param name an element name. - * @return A reference of AutoConnectElement class. - */ -template<> -AutoConnectElementBasis& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - return *(reinterpret_cast(elm)); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectButtonBasis element. - * @param name An element name. - * @return A reference of AutoConnectButton class. - */ -template<> -AutoConnectButtonBasis& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Button) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectCheckboxBasis element. - * @param name An element name. - * @return A reference of AutoConnectCheckbox class. - */ -template<> -AutoConnectCheckboxBasis& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Checkbox) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectFileBasis element. - * @param name An element name. - * @return A reference of AutoConnectFile class. - */ -template<> -AutoConnectFileBasis& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_File) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectInputBasis element. - * @param name An element name. - * @return A reference of AutoConnectInput class. - */ -template<> -AutoConnectInputBasis& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Input) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectRadioBasis element. - * @param name An element name. - * @return A reference of AutoConnectRadio class. - */ -template<> -AutoConnectRadioBasis& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Radio) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectSelectBasis element. - * @param name An element name. - * @return A reference of AutoConnectSelect class. - */ -template<> -AutoConnectSelectBasis& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Select) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectSubmitBasis element. - * @param name An element name. - * @return A reference of AutoConnectSubmit class. - */ -template<> -AutoConnectSubmitBasis& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Submit) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectTextBasis element. - * @param name An element name. - * @return A reference of AutoConnectText class. - */ -template<> -AutoConnectTextBasis& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Text) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -#else - -/** - * Get AutoConnectElementJson element. - * @param name an element name. - * @return A reference of AutoConnectElement class. - */ -template<> -AutoConnectElementJson& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - return *(reinterpret_cast(elm)); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectButtonJson element. - * @param name An element name. - * @return A reference of AutoConnectButton class. - */ -template<> -AutoConnectButtonJson& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Button) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectCheckboxJson element. - * @param name An element name. - * @return A reference of AutoConnectCheckbox class. - */ -template<> -AutoConnectCheckboxJson& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Checkbox) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectFile element. - * @param name An element name. - * @return A reference of AutoConnectFile class. - */ -template<> -AutoConnectFileJson& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_File) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectInputJson element. - * @param name An element name. - * @return A reference of AutoConnectInput class. - */ -template<> -AutoConnectInputJson& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Input) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectRadioJson element. - * @param name An element name. - * @return A reference of AutoConnectRadio class. - */ -template<> -AutoConnectRadioJson& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Radio) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectSelectJson element. - * @param name An element name. - * @return A reference of AutoConnectSelect class. - */ -template<> -AutoConnectSelectJson& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Select) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectSubmitJson element. - * @param name An element name. - * @return A reference of AutoConnectSubmit class. - */ -template<> -AutoConnectSubmitJson& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Submit) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} - -/** - * Get AutoConnectTextJson element. - * @param name An element name. - * @return A reference of AutoConnectText class. - */ -template<> -AutoConnectTextJson& AutoConnectAux::getElement(const String& name) { - AutoConnectElement* elm = getElement(name); - if (elm) { - if (elm->typeOf() == AC_Text) - return *(reinterpret_cast(elm)); - else - AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); - } - return reinterpret_cast(_nullElement()); -} +#ifdef AUTOCONNECT_USE_JSON /** * Load AutoConnectAux page from JSON description stored in the sketch. @@ -778,35 +476,29 @@ AutoConnectTextJson& AutoConnectAux::getElement(const String& name) { * @return true Successfully loaded. */ bool AutoConnect::load(const String& aux) { - DynamicJsonBuffer jsonBuffer(AUTOCONNECT_JSON_BUFFER_SIZE); - JsonVariant jv = jsonBuffer.parse(aux); - return _load(jv); + return _parseJson(aux); } /** -* Load AutoConnectAux page from JSON description stored in PROGMEM. -* This function can load AutoConnectAux for multiple AUX pages written -* in JSON and is registered in AutoConnect. -* @param aux JSON description to be load. -* @return true Successfully loaded. -*/ + * Load AutoConnectAux page from JSON description stored in PROGMEM. + * This function can load AutoConnectAux for multiple AUX pages written + * in JSON and is registered in AutoConnect. + * @param aux JSON description to be load. + * @return true Successfully loaded. + */ bool AutoConnect::load(const __FlashStringHelper* aux) { - DynamicJsonBuffer jsonBuffer(AUTOCONNECT_JSON_BUFFER_SIZE); - JsonVariant jv = jsonBuffer.parse(aux); - return _load(jv); + return _parseJson(aux); } /** -* Load AutoConnectAux page from JSON description from the stream. -* This function can load AutoConnectAux for multiple AUX pages written -* in JSON and is registered in AutoConnect. -* @param aux Stream for read AutoConnectAux elements. -* @return true Successfully loaded. -*/ + * Load AutoConnectAux page from JSON description from the stream. + * This function can load AutoConnectAux for multiple AUX pages written + * in JSON and is registered in AutoConnect. + * @param aux Stream for read AutoConnectAux elements. + * @return true Successfully loaded. + */ bool AutoConnect::load(Stream& aux) { - DynamicJsonBuffer jsonBuffer(AUTOCONNECT_JSON_BUFFER_SIZE); - JsonVariant jv = jsonBuffer.parse(aux); - return _load(jv); + return _parseJson(aux); } /** @@ -815,34 +507,29 @@ bool AutoConnect::load(Stream& aux) { * @return true Successfully loaded. */ bool AutoConnect::_load(JsonVariant& auxJson) { - bool rc = auxJson.success(); - if (rc) { - if (auxJson.is()) { - JsonArray& jb = auxJson.as(); - for (JsonObject& auxJson : jb) { - AutoConnectAux* newAux = new AutoConnectAux; - if (newAux->_load(auxJson)) - join(*newAux); - else { - delete newAux; - rc = false; - break; - } - } - } - else { - JsonObject& jb = auxJson.as(); + bool rc = true; + if (auxJson.is()) { + ArduinoJsonArray jb = auxJson.as(); + for (ArduinoJsonObject auxJson : jb) { AutoConnectAux* newAux = new AutoConnectAux; - if (newAux->_load(jb)) + if (newAux->_load(auxJson)) join(*newAux); else { delete newAux; rc = false; + break; } } } else { - AC_DBG("JSON parse error\n"); + ArduinoJsonObject jb = auxJson.as(); + AutoConnectAux* newAux = new AutoConnectAux; + if (newAux->_load(jb)) + join(*newAux); + else { + delete newAux; + rc = false; + } } return rc; } @@ -854,7 +541,7 @@ bool AutoConnect::_load(JsonVariant& auxJson) { */ AutoConnectElement* AutoConnectAux::_createElement(const JsonObject& json) { AutoConnectElement* elm = nullptr; - String type = json.get(F(AUTOCONNECT_JSON_KEY_TYPE)); + String type = json[F(AUTOCONNECT_JSON_KEY_TYPE)].as(); switch (_asElementType(type)) { case AC_Element: @@ -906,9 +593,7 @@ AutoConnectElement* AutoConnectAux::_createElement(const JsonObject& json) { * @return false Invalid JSON data occurred. */ bool AutoConnectAux::load(const String& in) { - DynamicJsonBuffer jsonBuffer(AUTOCONNECT_JSON_BUFFER_SIZE); - JsonObject& jb = jsonBuffer.parseObject(in); - return _load(jb); + return _parseJson(in); } /** @@ -920,9 +605,7 @@ bool AutoConnectAux::load(const String& in) { * @return false Invalid JSON data occurred. */ bool AutoConnectAux::load(const __FlashStringHelper* in) { - DynamicJsonBuffer jsonBuffer(AUTOCONNECT_JSON_BUFFER_SIZE); - JsonObject& jb = jsonBuffer.parseObject(in); - return _load(jb); + return _parseJson(in); } /** @@ -934,9 +617,7 @@ bool AutoConnectAux::load(const __FlashStringHelper* in) { * @return false Invalid JSON data occurred. */ bool AutoConnectAux::load(Stream& in) { - DynamicJsonBuffer jsonBuffer(AUTOCONNECT_JSON_BUFFER_SIZE); - JsonObject& jb = jsonBuffer.parseObject(in); - return _load(jb); + return _parseJson(in); } /** @@ -946,15 +627,10 @@ bool AutoConnectAux::load(Stream& in) { * @return false loading unsuccessful, JSON parsing error occurred. */ bool AutoConnectAux::_load(JsonObject& jb) { - if (!jb.success()) { - AC_DBG("json parse error\n"); - return false; - } - - _title = jb.get(F(AUTOCONNECT_JSON_KEY_TITLE)); - _uriStr = jb.get(F(AUTOCONNECT_JSON_KEY_URI)); + _title = jb[F(AUTOCONNECT_JSON_KEY_TITLE)].as(); + _uriStr = jb[F(AUTOCONNECT_JSON_KEY_URI)].as(); _uri = _uriStr.c_str(); - _menu = jb.get(F(AUTOCONNECT_JSON_KEY_MENU)); + _menu = jb[F(AUTOCONNECT_JSON_KEY_MENU)].as(); JsonVariant elements = jb[F(AUTOCONNECT_JSON_KEY_ELEMENT)]; (void)_loadElement(elements, ""); return true; @@ -971,49 +647,41 @@ bool AutoConnectAux::_load(JsonObject& jb) { * @return A reference of loaded AutoConnectElement instance. */ bool AutoConnectAux::loadElement(const String& in, const String& name) { - DynamicJsonBuffer jsonBuffer(AUTOCONNECT_JSON_BUFFER_SIZE); - JsonVariant jb = jsonBuffer.parse(in); - return _loadElement(jb, name); + return _parseElement(in, name); } bool AutoConnectAux::loadElement(const __FlashStringHelper* in, const String& name) { - DynamicJsonBuffer jsonBuffer(AUTOCONNECT_JSON_BUFFER_SIZE); - JsonVariant jb = jsonBuffer.parse(in); - return _loadElement(jb, name); + return _parseElement(in, name); } bool AutoConnectAux::loadElement(Stream& in, const String& name) { - DynamicJsonBuffer jsonBuffer(AUTOCONNECT_JSON_BUFFER_SIZE); - JsonVariant jb = jsonBuffer.parse(in); - return _loadElement(jb, name); + return _parseElement(in, name); } bool AutoConnectAux::_loadElement(JsonVariant& jb, const String& name) { - bool rc = jb.success(); - if (rc) { - if (jb.is()) { - JsonArray& elements = jb.as(); - for (JsonObject& element : elements) { - AutoConnectElement& elm = _loadElement(element, name); - if (!elm.name.length()) { - rc = false; - break; - } - } - } - else { - JsonObject& element = jb.as(); + bool rc = true; + if (jb.is()) { + ArduinoJsonArray elements = jb.as(); + for (ArduinoJsonObject element : elements) { AutoConnectElement& elm = _loadElement(element, name); - if (!elm.name.length()) + if (!elm.name.length()) { rc = false; + break; + } } } + else { + ArduinoJsonObject element = jb.as(); + AutoConnectElement& elm = _loadElement(element, name); + if (!elm.name.length()) + rc = false; + } return rc; } AutoConnectElement& AutoConnectAux::_loadElement(JsonObject& element, const String& name) { AutoConnectElement* auxElm = nullptr; - String elmName = element.get(F(AUTOCONNECT_JSON_KEY_NAME)); + String elmName = element[F(AUTOCONNECT_JSON_KEY_NAME)].as(); if (!name.length() || name.equalsIgnoreCase(elmName)) { // The specified element is defined in the JSON stream. // Loads from JSON object. @@ -1067,40 +735,43 @@ size_t AutoConnectAux::saveElement(Stream& out, std::vector const& names bufferSize += elm.getObjectSize(); break; } + // Round up to 16 boundary + bufferSize = bufferSize > 0 ? ((bufferSize + 16) & (~0xf)) : bufferSize; + // Serialization if (bufferSize > 0) { - DynamicJsonBuffer jb(bufferSize); + ArduinoJsonBuffer jb(bufferSize); if (amount == 1) { - JsonObject& element = jb.createObject(); + ArduinoJsonObject element = ARDUINOJSON_CREATEOBJECT(jb); for (AutoConnectElement& elm : _addonElm) if (elm.name.equalsIgnoreCase(names[0])) { elm.serialize(element); break; } - size_n = element.printTo(out); + size_n = ARDUINOJSON_PRINT(element, out); } else if (amount == 0) { - JsonObject& json = jb.createObject(); + ArduinoJsonObject json = ARDUINOJSON_CREATEOBJECT(jb); json[F(AUTOCONNECT_JSON_KEY_TITLE)] = _title; json[F(AUTOCONNECT_JSON_KEY_URI)] = _uriStr; json[F(AUTOCONNECT_JSON_KEY_MENU)] = _menu; - JsonArray& elements = json.createNestedArray(F(AUTOCONNECT_JSON_KEY_ELEMENT)); + ArduinoJsonArray elements = json.createNestedArray(F(AUTOCONNECT_JSON_KEY_ELEMENT)); for (AutoConnectElement& elm : _addonElm) { - JsonObject& element = elements.createNestedObject(); + ArduinoJsonObject element = elements.createNestedObject(); elm.serialize(element); } - size_n = json.prettyPrintTo(out); + size_n = ARDUINOJSON_PRETTYPRINT(json, out); } else if (amount >= 2) { - JsonArray& elements = jb.createArray(); + ArduinoJsonArray elements = ARDUINOJSON_CREATEARRAY(jb); for (String name : names) for (AutoConnectElement& elm : _addonElm) if (elm.name.equalsIgnoreCase(name)) { - JsonObject& element = elements.createNestedObject(); + ArduinoJsonObject element = elements.createNestedObject(); elm.serialize(element); break; } - size_n = elements.prettyPrintTo(out); + size_n = ARDUINOJSON_PRETTYPRINT(elements, out); } } return size_n; @@ -1136,4 +807,4 @@ ACElement_t AutoConnectAux::_asElementType(const String& type) { return t; } -#endif // AUTOCONNECT_USE_JSON +#endif // !AUTOCONNECT_USE_JSON diff --git a/src/AutoConnectAux.h b/src/AutoConnectAux.h index 352e30d..5d7ac7d 100644 --- a/src/AutoConnectAux.h +++ b/src/AutoConnectAux.h @@ -88,8 +88,12 @@ class AutoConnectAux : public PageBuilder { static AutoConnectElement& _nullElement(void); /**< A static returning value as invalid */ #ifdef AUTOCONNECT_USE_JSON + template + bool _parseJson(T in); bool _load(JsonObject& in); /**< Load all elements from JSON object */ bool _loadElement(JsonVariant& in, const String& name); /**< Load an element as specified name from JSON object */ + template + bool _parseElement(T in, const String& name); AutoConnectElement& _loadElement(JsonObject& in, const String& name); /**< Load an element as specified name from JSON object */ AutoConnectElement* _createElement(const JsonObject& json); /**< Create an AutoConnectElement instance from JSON object */ static ACElement_t _asElementType(const String& type); /**< Convert a string of element type to the enumeration value */ diff --git a/src/AutoConnectAuxImpl.h b/src/AutoConnectAuxImpl.h new file mode 100644 index 0000000..799afbe --- /dev/null +++ b/src/AutoConnectAuxImpl.h @@ -0,0 +1,416 @@ +/** + * Implementation of template functions of AutoConnect and AutoConnectAux. + * This implementation instantiates completely the void AutoConnectElement + * as each type and also absorbs interface differences due to ArduinoJson + * version differences. + * @file AutoConnectAuxImpl.h + * @author hieromon@gmail.com + * @version 0.9.8 + * @date 2019-03-21 + * @copyright MIT license. + */ + +#ifndef _AUTOCONNECTAUXIMPL_H_ +#define _AUTOCONNECTAUXIMPL_H_ + +#include "AutoConnectDefs.h" + +#ifndef AUTOCONNECT_USE_JSON + +/** + * Get AutoConnectElementBasis element. + * @param name an element name. + * @return A reference of AutoConnectElement class. + */ +template<> +AutoConnectElementBasis& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + return *(reinterpret_cast(elm)); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectButtonBasis element. + * @param name An element name. + * @return A reference of AutoConnectButton class. + */ +template<> +AutoConnectButtonBasis& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Button) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectCheckboxBasis element. + * @param name An element name. + * @return A reference of AutoConnectCheckbox class. + */ +template<> +AutoConnectCheckboxBasis& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Checkbox) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectFileBasis element. + * @param name An element name. + * @return A reference of AutoConnectFile class. + */ +template<> +AutoConnectFileBasis& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_File) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectInputBasis element. + * @param name An element name. + * @return A reference of AutoConnectInput class. + */ +template<> +AutoConnectInputBasis& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Input) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectRadioBasis element. + * @param name An element name. + * @return A reference of AutoConnectRadio class. + */ +template<> +AutoConnectRadioBasis& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Radio) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectSelectBasis element. + * @param name An element name. + * @return A reference of AutoConnectSelect class. + */ +template<> +AutoConnectSelectBasis& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Select) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectSubmitBasis element. + * @param name An element name. + * @return A reference of AutoConnectSubmit class. + */ +template<> +AutoConnectSubmitBasis& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Submit) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectTextBasis element. + * @param name An element name. + * @return A reference of AutoConnectText class. + */ +template<> +AutoConnectTextBasis& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Text) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +#else + +/** + * Parse and load a JSON document which marks up multiple custom web + * pages. The compiler instantiates this template according to the stored + * data type that contains the JSON document. + * This template also generates different parsing function calls + * depending on the ArduinoJson version. + * @param T An object type of the JSON document which must be a + * passable object to ArduinoJson. + * @param in An instance of a source JSON document to load. + */ +template +bool AutoConnect::_parseJson(T in) { + ArduinoJsonBuffer jsonBuffer(AUTOCONNECT_JSONBUFFER_PRIMITIVE_SIZE); + JsonVariant jv; +#if ARDUINOJSON_VERSION_MAJOR<=5 + jv = jsonBuffer.parse(in); + if (!jv.success()) { + AC_DBG("JSON parse error\n"); + return false; + } +#else + DeserializationError err = deserializeJson(jsonBuffer, in); + if (err) { + AC_DBG("Deserialize error:%s\n", err.c_str()); + return false; + } + jv = jsonBuffer.as(); +#endif + return _load(jv); +} + +/** + * Parse and load a JSON document which marks up a custom web page. + * The compiler instantiates this template according to the stored data + * type that contains the JSON document. + * This template also generates different parsing function calls + * depending on the ArduinoJson version. + * @param T An object type of the JSON document which must be a + * passable object to ArduinoJson. + * @param in An instance of a source JSON document to load. + */ +template +bool AutoConnectAux::_parseJson(T in) { + ArduinoJsonBuffer jsonBuffer(AUTOCONNECT_JSONBUFFER_PRIMITIVE_SIZE); +#if ARDUINOJSON_VERSION_MAJOR<=5 + JsonObject& jb = jsonBuffer.parseObject(in); + if (!jb.success()) { + AC_DBG("JSON parse error\n"); + return false; + } +#else + DeserializationError err = deserializeJson(jsonBuffer, in); + if (err) { + AC_DBG("Deserialize:%s\n", err.c_str()); + return false; + } + JsonObject jb = jsonBuffer.as(); +#endif + return _load(jb); +} + +/** + * Parse and load a JSON document which declares one of the AutoConnectElement. + * The compiler instantiates this template according to the stored data + * type that contains the JSON document. + * This template also generates different parsing function calls + * depending on the ArduinoJson version. + * @param T An object type of the JSON document which must be a + * passable object to ArduinoJson. + * @param in An instance of a source JSON document to load. + */ +template +bool AutoConnectAux::_parseElement(T in, const String& name) { + ArduinoJsonBuffer jsonBuffer(AUTOCONNECT_JSONBUFFER_PRIMITIVE_SIZE); + JsonVariant jb; +#if ARDUINOJSON_VERSION_MAJOR<=5 + jb = jsonBuffer.parse(in); + if (!jb.success()) { + AC_DBG("JSON parse error\n"); + return false; + } +#else + DeserializationError err = deserializeJson(jsonBuffer, in); + if (err) { + AC_DBG("Deserialize:%s\n", err.c_str()); + return false; + } + jb = jsonBuffer.as(); +#endif + return _loadElement(jb, name); +} + +/** + * Get AutoConnectElementJson element. + * @param name an element name. + * @return A reference of AutoConnectElement class. + */ +template<> +AutoConnectElementJson& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + return *(reinterpret_cast(elm)); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectButtonJson element. + * @param name An element name. + * @return A reference of AutoConnectButton class. + */ +template<> +AutoConnectButtonJson& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Button) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectCheckboxJson element. + * @param name An element name. + * @return A reference of AutoConnectCheckbox class. + */ +template<> +AutoConnectCheckboxJson& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Checkbox) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectFile element. + * @param name An element name. + * @return A reference of AutoConnectFile class. + */ +template<> +AutoConnectFileJson& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_File) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectInputJson element. + * @param name An element name. + * @return A reference of AutoConnectInput class. + */ +template<> +AutoConnectInputJson& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Input) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectRadioJson element. + * @param name An element name. + * @return A reference of AutoConnectRadio class. + */ +template<> +AutoConnectRadioJson& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Radio) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectSelectJson element. + * @param name An element name. + * @return A reference of AutoConnectSelect class. + */ +template<> +AutoConnectSelectJson& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Select) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectSubmitJson element. + * @param name An element name. + * @return A reference of AutoConnectSubmit class. + */ +template<> +AutoConnectSubmitJson& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Submit) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +/** + * Get AutoConnectTextJson element. + * @param name An element name. + * @return A reference of AutoConnectText class. + */ +template<> +AutoConnectTextJson& AutoConnectAux::getElement(const String& name) { + AutoConnectElement* elm = getElement(name); + if (elm) { + if (elm->typeOf() == AC_Text) + return *(reinterpret_cast(elm)); + else + AC_DBG("Element<%s> type mismatch<%d>\n", name.c_str(), elm->typeOf()); + } + return reinterpret_cast(_nullElement()); +} + +#endif // !AUTOCONNECT_USE_JSON + +#endif // !_AUTOCONNECTAUXIMPL_H_ diff --git a/src/AutoConnectDefs.h b/src/AutoConnectDefs.h index 90b30a1..dc35053 100644 --- a/src/AutoConnectDefs.h +++ b/src/AutoConnectDefs.h @@ -149,6 +149,18 @@ #define AUTOCONNECT_SD_SPEED 4000000 #endif #endif // !AUTOCONNECT_SD_SPEED + +// ArduinoJson buffer size +#ifndef AUTOCONNECT_JSONBUFFER_SIZE +#define AUTOCONNECT_JSONBUFFER_SIZE 256 +#endif // !AUTOCONNECT_JSONBUFFER_SIZE +#ifndef AUTOCONNECT_JSONDOCUMENT_SIZE +#define AUTOCONNECT_JSONDOCUMENT_SIZE (8 * 1024) +#endif // !AUTOCONNECT_JSONDOCUMENT_SIZE +#ifndef AUTOCONNECT_JSONPSRAM_SIZE +#define AUTOCONNECT_JSONPSRAM_SIZE (16* 1024) +#endif // !AUTOCONNECT_JSONPSRAM_SIZE + // Explicitly avoiding unused warning with token handler of PageBuilder #define AC_UNUSED(expr) do { (void)(expr); } while (0) diff --git a/src/AutoConnectElementBasis.h b/src/AutoConnectElementBasis.h index a718b7e..58643f1 100644 --- a/src/AutoConnectElementBasis.h +++ b/src/AutoConnectElementBasis.h @@ -52,6 +52,10 @@ class AutoConnectElementBasis { virtual ~AutoConnectElementBasis() {} virtual const String toHTML(void) const { return value; } ACElement_t typeOf(void) const { return _type; } +#ifndef AUTOCONNECT_USE_JSON + template + T& as(void); +#endif String name; /**< Element name */ String value; /**< Element value */ @@ -249,4 +253,66 @@ class AutoConnectTextBasis : virtual public AutoConnectElementBasis { String format; /**< C string that contains the text to be written */ }; +#ifndef AUTOCONNECT_USE_JSON +/** + * Casts only a class derived from the AutoConnectElement class to the + * actual element class. + */ +template<> +inline AutoConnectButtonBasis& AutoConnectElementBasis::as(void) { + if (typeOf() != AC_Button) + AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); + return *(reinterpret_cast(this)); +} + +template<> +inline AutoConnectCheckboxBasis& AutoConnectElementBasis::as(void) { + if (typeOf() != AC_Checkbox) + AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); + return *(reinterpret_cast(this)); +} + +template<> +inline AutoConnectFileBasis& AutoConnectElementBasis::as(void) { + if (typeOf() != AC_File) + AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); + return *(reinterpret_cast(this)); +} + +template<> +inline AutoConnectInputBasis& AutoConnectElementBasis::as(void) { + if (typeOf() != AC_Input) + AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); + return *(reinterpret_cast(this)); +} + +template<> +inline AutoConnectRadioBasis& AutoConnectElementBasis::as(void) { + if (typeOf() != AC_Radio) + AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); + return *(reinterpret_cast(this)); +} + +template<> +inline AutoConnectSelectBasis& AutoConnectElementBasis::as(void) { + if (typeOf() != AC_Select) + AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); + return *(reinterpret_cast(this)); +} + +template<> +inline AutoConnectSubmitBasis& AutoConnectElementBasis::as(void) { + if (typeOf() != AC_Submit) + AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); + return *(reinterpret_cast(this)); +} + +template<> +inline AutoConnectTextBasis& AutoConnectElementBasis::as(void) { + if (typeOf() != AC_Text) + AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); + return *(reinterpret_cast(this)); +} +#endif + #endif // _AUTOCONNECTELEMENTBASIS_H_ diff --git a/src/AutoConnectElementBasisImpl.h b/src/AutoConnectElementBasisImpl.h index 8437da0..43c4cf3 100644 --- a/src/AutoConnectElementBasisImpl.h +++ b/src/AutoConnectElementBasisImpl.h @@ -19,6 +19,66 @@ #endif #include "AutoConnectElementBasis.h" +/** + * Casts only a class derived from the AutoConnectElement class to the + * actual element class. + */ +// template<> +// inline AutoConnectButtonBasis& AutoConnectElementBasis::as(void) { +// if (typeOf() != AC_Button) +// AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); +// return reinterpret_cast(*this); +// } + +// template<> +// inline AutoConnectCheckboxBasis& AutoConnectElementBasis::as(void) { +// if (typeOf() != AC_Checkbox) +// AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); +// return reinterpret_cast(*this); +// } + +// template<> +// inline AutoConnectFileBasis& AutoConnectElementBasis::as(void) { +// if (typeOf() != AC_File) +// AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); +// return reinterpret_cast(*this); +// } + +// template<> +// inline AutoConnectInputBasis& AutoConnectElementBasis::as(void) { +// if (typeOf() != AC_Input) +// AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); +// return reinterpret_cast(*this); +// } + +// template<> +// inline AutoConnectRadioBasis& AutoConnectElementBasis::as(void) { +// if (typeOf() != AC_Radio) +// AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); +// return reinterpret_cast(*this); +// } + +// template<> +// inline AutoConnectSelectBasis& AutoConnectElementBasis::as(void) { +// if (typeOf() != AC_Select) +// AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); +// return reinterpret_cast(*this); +// } + +// template<> +// inline AutoConnectSubmitBasis& AutoConnectElementBasis::as(void) { +// if (typeOf() != AC_Submit) +// AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); +// return reinterpret_cast(*this); +// } + +// template<> +// inline AutoConnectTextBasis& AutoConnectElementBasis::as(void) { +// if (typeOf() != AC_Text) +// AC_DBG("%s mismatched type as <%d>\n", name.c_str(), (int)typeOf()); +// return reinterpret_cast(*this); +// } + /** * Generate an HTML