Merge pull request #3878 from TD-er/bugfix/serve_files_memory_reduction

[Cleanup] Use less resources serving files from FS
This commit is contained in:
TD-er
2021-12-27 16:51:17 +01:00
committed by GitHub
16 changed files with 309 additions and 226 deletions
+5 -2
View File
@@ -11,8 +11,11 @@
#include "../Helpers/ESPEasy_time_calc.h"
#define CHUNKED_BUFFER_SIZE 400
#ifdef ESP8266
#define CHUNKED_BUFFER_SIZE 512
#else
#define CHUNKED_BUFFER_SIZE 1360
#endif
Web_StreamingBuffer::Web_StreamingBuffer(void) : lowMemorySkip(false),
initialRam(0), beforeTXRam(0), duringTXRam(0), finalRam(0), maxCoreUsage(0),
+36 -32
View File
@@ -1366,56 +1366,60 @@ size_t SpiffsUsedBytes() {
}
size_t SpiffsTotalBytes() {
size_t result = 1; // Do not output 0, this may be used in divisions.
#ifdef ESP32
result = ESPEASY_FS.totalBytes();
#endif // ifdef ESP32
#ifdef ESP8266
fs::FSInfo fs_info;
ESPEASY_FS.info(fs_info);
result = fs_info.totalBytes;
#endif // ifdef ESP8266
static size_t result = 1; // Do not output 0, this may be used in divisions.
if (result == 1) {
#ifdef ESP32
result = ESPEASY_FS.totalBytes();
#endif // ifdef ESP32
#ifdef ESP8266
fs::FSInfo fs_info;
ESPEASY_FS.info(fs_info);
result = fs_info.totalBytes;
#endif // ifdef ESP8266
}
return result;
}
size_t SpiffsBlocksize() {
size_t result = 8192; // Some default viable for most 1 MB file systems
#ifdef ESP32
result = 8192; // Just assume 8k, since we cannot query it
#endif // ifdef ESP32
#ifdef ESP8266
fs::FSInfo fs_info;
ESPEASY_FS.info(fs_info);
result = fs_info.blockSize;
#endif // ifdef ESP8266
static size_t result = 1;
if (result == 1) {
#ifdef ESP32
result = 8192; // Just assume 8k, since we cannot query it
#endif // ifdef ESP32
#ifdef ESP8266
fs::FSInfo fs_info;
ESPEASY_FS.info(fs_info);
result = fs_info.blockSize;
#endif // ifdef ESP8266
}
return result;
}
size_t SpiffsPagesize() {
size_t result = 256; // Most common
#ifdef ESP32
result = 256; // Just assume 256, since we cannot query it
#endif // ifdef ESP32
#ifdef ESP8266
fs::FSInfo fs_info;
ESPEASY_FS.info(fs_info);
result = fs_info.pageSize;
#endif // ifdef ESP8266
static size_t result = 1;
if (result == 1) {
#ifdef ESP32
result = 256; // Just assume 256, since we cannot query it
#endif // ifdef ESP32
#ifdef ESP8266
fs::FSInfo fs_info;
ESPEASY_FS.info(fs_info);
result = fs_info.pageSize;
#endif // ifdef ESP8266
}
return result;
}
size_t SpiffsFreeSpace() {
int freeSpace = SpiffsTotalBytes() - SpiffsUsedBytes();
const size_t blocksize = SpiffsBlocksize();
if (freeSpace < static_cast<int>(2 * SpiffsBlocksize())) {
if (freeSpace < static_cast<int>(2 * blocksize)) {
// Not enough free space left to store anything
// There needs to be minimum of 2 free blocks.
return 0;
}
return freeSpace - 2 * SpiffsBlocksize();
return freeSpace - 2 * blocksize;
}
bool SpiffsFull() {
+42 -15
View File
@@ -25,6 +25,7 @@
#ifdef ESP32
#include <soc/soc.h>
#include <soc/efuse_reg.h>
#include <rom/spi_flash.h>
#endif
/********************************************************************************************\
@@ -480,21 +481,30 @@ int espeasy_analogRead(int pin, bool readAsTouch) {
Hardware information
\*********************************************************************************************/
uint32_t getFlashChipId() {
uint32_t flashChipId = 0;
// Cache since size does not change
static uint32_t flashChipId = 0;
if (flashChipId == 0) {
#ifdef ESP32
//esp_flash_read_id(nullptr, &flashChipId);
flashChipId = g_rom_flashchip.device_id;
// esp_flash_read_id(nullptr, &flashChipId);
#elif defined(ESP8266)
flashChipId = ESP.getFlashChipId();
flashChipId = ESP.getFlashChipId();
#endif
}
return flashChipId;
}
uint32_t getFlashRealSizeInBytes() {
#if defined(ESP32)
return ESP.getFlashChipSize();
#else // if defined(ESP32)
return ESP.getFlashChipRealSize(); // ESP.getFlashChipSize();
#endif // if defined(ESP32)
// Cache since size does not change
static uint32_t res = 0;
if (res == 0) {
#if defined(ESP32)
res = ESP.getFlashChipSize();
#else // if defined(ESP32)
res = ESP.getFlashChipRealSize(); // ESP.getFlashChipSize();
#endif // if defined(ESP32)
}
return res;
}
@@ -519,17 +529,17 @@ uint8_t getFlashChipVendorId() {
return ESP.getFlashChipVendorId();
#else // ifdef PUYA_SUPPORT
# if defined(ESP8266)
uint32_t flashChipId = ESP.getFlashChipId();
// Cache since size does not change
static uint32_t flashChipId = ESP.getFlashChipId();
return flashChipId & 0x000000ff;
# elif defined(ESP32)
return 0xFF; // Not an existing function for ESP32
# endif // if defined(ESP8266)
#endif // ifdef PUYA_SUPPORT
return 0xFF; // Not an existing function for ESP32
}
bool flashChipVendorPuya() {
uint8_t vendorId = getFlashChipVendorId();
const uint8_t vendorId = getFlashChipVendorId();
return vendorId == 0x85; // 0x146085 PUYA
}
@@ -550,13 +560,17 @@ uint32_t getChipId() {
}
uint8_t getChipCores() {
uint8_t cores = 1;
#ifdef ESP32
#ifdef ESP8266
return 1;
#else
static uint8_t cores = 0;
if (cores == 0) {
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
cores = chip_info.cores;
#endif
}
return cores;
#endif
}
const __FlashStringHelper * getChipModel() {
@@ -599,6 +613,19 @@ uint8_t getChipRevision() {
return rev;
}
uint32_t getSketchSize() {
// Cache the value as this never changes during run time.
static uint32_t res = ESP.getSketchSize();
return res;
}
uint32_t getFreeSketchSpace() {
// Cache the value as this never changes during run time.
static uint32_t res = ESP.getFreeSketchSpace();
return res;
}
#ifdef ESP8266
void readBootCause() {
lastBootCause = BOOT_CAUSE_MANUAL_REBOOT;
+4
View File
@@ -82,6 +82,10 @@ const __FlashStringHelper * getChipModel();
uint8_t getChipRevision();
uint32_t getSketchSize();
uint32_t getFreeSketchSpace();
/********************************************************************************************\
Boot information
\*********************************************************************************************/
+2 -2
View File
@@ -12,8 +12,8 @@ bool OTA_possible(uint32_t& maxSketchSize, bool& use2step) {
// Compute the current free space and sketch size, rounded to 4k blocks.
// These block bounaries are needed for erasing a full block on flash.
const uint32_t freeSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
const uint32_t currentSketchSize = (ESP.getSketchSize() + 0x1000) & 0xFFFFF000;
const uint32_t freeSketchSpace = (getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
const uint32_t currentSketchSize = (getSketchSize() + 0x1000) & 0xFFFFF000;
const uint32_t smallestOtaImageSizeNeeded = (((SMALLEST_OTA_IMAGE + 16) + 0x1000) & 0xFFFFF000);
const bool otaPossible = freeSketchSpace >= smallestOtaImageSizeNeeded;
use2step = freeSketchSpace < currentSketchSize; // Assume the new image has the same size.
+22 -15
View File
@@ -3,6 +3,7 @@
#include "../Globals/Cache.h"
#include "../Helpers/ESPEasy_Storage.h"
#include "../WebServer/HTML_wrappers.h"
#include "../WebServer/LoadFromFS.h"
String generate_external_URL(const String& fname) {
String url;
@@ -14,27 +15,28 @@ String generate_external_URL(const String& fname) {
void serve_CSS() {
String url = F("esp.css");
if (!fileExists(url))
const String cssFile = F("esp.css");
if (fileExists(cssFile))
{
#ifndef WEBSERVER_CSS
url = generate_external_URL(F("espeasy_default.css"));
#else
addHtml(F("<style>"));
// Send CSS in chunks
TXBuffer.addFlashString((PGM_P)FPSTR(DATA_ESPEASY_DEFAULT_MIN_CSS));
streamFromFS(cssFile);
addHtml(F("</style>"));
return;
#endif
}
#ifndef WEBSERVER_CSS
addHtml(F("<link"));
addHtmlAttribute(F("rel"), F("stylesheet"));
addHtmlAttribute(F("type"), F("text/css"));
addHtmlAttribute(F("href"), url);
addHtmlAttribute(F("href"), generate_external_URL(F("espeasy_default.css")));
addHtml('/');
addHtml('>');
#else
addHtml(F("<style>"));
// Send CSS in chunks
TXBuffer.addFlashString((PGM_P)FPSTR(DATA_ESPEASY_DEFAULT_MIN_CSS));
addHtml(F("</style>"));
#endif
}
void serve_favicon() {
@@ -112,10 +114,15 @@ void serve_JS(JSfiles_e JSfile) {
html_add_script_end();
return;
#endif
addHtml(F("<script"));
addHtml(F(" defer"));
addHtmlAttribute(F("src"), url);
addHtml('>');
html_add_script_end();
return;
}
addHtml(F("<script"));
addHtml(F(" defer"));
addHtmlAttribute(F("src"), url);
addHtml('>');
// Now stream the file directly from the file system.
html_add_script(false);
streamFromFS(url);
html_add_script_end();
}
+1 -3
View File
@@ -32,9 +32,7 @@ void handleNotFound() {
if (handle_rules_edit(web_server.uri())) { return; }
#endif
if (loadFromFS(true, web_server.uri())) { return; }
if (loadFromFS(false, web_server.uri())) { return; }
if (loadFromFS(web_server.uri())) { return; }
String message = F("URI: ");
message += web_server.uri();
message += F("\nMethod: ");
+30 -20
View File
@@ -23,27 +23,16 @@
// ********************************************************************************
// Web Interface custom page handler
// ********************************************************************************
boolean handle_custom(String path) {
// path is a deepcopy, since it will be changed.
bool handle_custom(const String& path) {
#ifndef BUILD_NO_RAM_TRACKER
checkRAM(F("handle_custom"));
#endif
if (!clientIPallowed()) { return false; }
#ifdef ESP8266
// For ESP32 remove the leading slash
path = path.substring(1);
#endif
// create a dynamic custom page, parsing task values into [<taskname>#<taskvalue>] placeholders and parsing %xx% system variables
fs::File dataFile = tryOpenFile(path.c_str(), "r");
#ifdef ESP8266
const bool dashboardPage = path.startsWith(F("dashboard"));
#endif
#ifdef ESP32
const bool dashboardPage = path.startsWith(F("/dashboard"));
#endif
const bool dashboardPage = path.startsWith(F("dashboard")) || path.startsWith(F("/dashboard"));
if (!dataFile && !dashboardPage) {
return false; // unknown file that does not exist...
@@ -147,14 +136,35 @@ boolean handle_custom(String path) {
if (dataFile)
{
String page;
page.reserve(dataFile.size());
while (dataFile.available()) {
page += ((char)dataFile.read());
// Read the file per line and serve per line to reduce amount of memory needed.
int available = dataFile.available();
String line;
line.reserve(128);
while (available > 0) {
uint32_t chunksize = 64;
if (available < chunksize) {
chunksize = available;
}
uint8_t buf[64] = {0};
const size_t read = dataFile.read(buf, chunksize);
if (read == chunksize) {
for (uint32_t i = 0; i < chunksize; ++i) {
const char c = (char)buf[i];
line += c;
if (c == '\n') {
addHtml(parseTemplate(line));
line.clear();
line.reserve(128);
}
}
available = dataFile.available();
} else {
available = 0;
}
}
if (!line.isEmpty()) {
addHtml(parseTemplate(line));
}
addHtml(parseTemplate(page));
dataFile.close();
}
else // if the requestef file does not exist, create a default action in case the page is named "dashboard*"
+1 -1
View File
@@ -7,7 +7,7 @@
// ********************************************************************************
// Web Interface custom page handler
// ********************************************************************************
boolean handle_custom(String path);
bool handle_custom(const String& path);
#endif
#endif
+121 -68
View File
@@ -1,46 +1,66 @@
#include "../WebServer/LoadFromFS.h"
#include "../WebServer/WebServer.h"
#include "../WebServer/CustomPage.h"
#include "../Globals/RamTracker.h"
#include "../Helpers/ESPEasy_Storage.h"
#include "../Helpers/Network.h"
#ifdef FEATURE_SD
#include <SD.h>
#endif
#include "../WebServer/CustomPage.h"
#include "../WebServer/HTML_wrappers.h"
#include "../WebServer/WebServer.h"
bool match_ext(const String& path, const __FlashStringHelper * ext) {
return (path.endsWith(ext) || path.endsWith(String(ext) + F(".gz")));
#ifdef FEATURE_SD
# include <SD.h>
#endif // ifdef FEATURE_SD
bool match_ext(const String& path, const __FlashStringHelper *ext) {
return path.endsWith(ext) || path.endsWith(String(ext) + F(".gz"));
}
bool gzipEncoded(const String& path) {
return path.endsWith(F(".gz"));
}
// ********************************************************************************
// Web Interface server web file from FS
// ********************************************************************************
bool loadFromFS(boolean spiffs, String path) {
// path is a deepcopy, since it will be changed here.
#ifndef BUILD_NO_RAM_TRACKER
checkRAM(F("loadFromFS"));
#endif
statusLED(true);
String dataType = F("text/plain");
bool mustCheckCredentials = false;
String fileFromUrl(String path) {
const int questionmarkPos = path.indexOf('?');
if (questionmarkPos >= 0) {
path = path.substring(0, questionmarkPos);
}
// First prepend slash
if (!path.startsWith(F("/"))) {
path = String(F("/")) + path;
}
if (path.endsWith(F("/"))) { path += F("index.htm"); }
#ifdef ESP8266
// Remove leading slash to generate filename from it.
if (path.startsWith(F("/"))) {
path = path.substring(1);
}
#endif
return path;
}
// ********************************************************************************
// Web Interface server web file from FS
// ********************************************************************************
bool loadFromFS(String path) {
// path is a deepcopy, since it will be changed here.
#ifndef BUILD_NO_RAM_TRACKER
checkRAM(F("loadFromFS"));
#endif // ifndef BUILD_NO_RAM_TRACKER
statusLED(true);
const __FlashStringHelper* dataType = F("text/plain");
bool mustCheckCredentials = false;
path = fileFromUrl(path);
if (path.endsWith(F(".src"))) { path = path.substring(0, path.lastIndexOf(".")); }
else if (match_ext(path, F(".htm")) || match_ext(path, F(".html"))) { dataType = F("text/html"); }
else if (match_ext(path, F(".css"))) { dataType = F("text/css"); }
@@ -52,15 +72,15 @@ bool loadFromFS(boolean spiffs, String path) {
else if (path.endsWith(F(".svg"))) { dataType = F("image/svg+xml"); }
else if (path.endsWith(F(".json"))) { dataType = F("application/json"); }
else if (path.endsWith(F(".txt")) ||
path.endsWith(F(".dat"))) {
path.endsWith(F(".dat"))) {
mustCheckCredentials = true;
dataType = F("application/octet-stream");
dataType = F("application/octet-stream");
}
#ifdef WEBSERVER_CUSTOM
else if (path.endsWith(F(".esp"))) {
return handle_custom(path);
return handle_custom(path);
}
#endif
#endif // ifdef WEBSERVER_CUSTOM
else {
mustCheckCredentials = true;
}
@@ -78,53 +98,86 @@ bool loadFromFS(boolean spiffs, String path) {
}
#endif // ifndef BUILD_NO_DEBUG
#if !defined(ESP32)
path = path.substring(1);
#endif // if !defined(ESP32)
fs::File f;
if (spiffs)
{
if (!fileExists(path)) {
return false;
}
fs::File dataFile = tryOpenFile(path.c_str(), "r");
if (!dataFile) {
return false;
}
// prevent reloading stuff on every click
web_server.sendHeader(F("Cache-Control"), F("max-age=3600, public"));
web_server.sendHeader(F("Vary"), "*");
web_server.sendHeader(F("ETag"), F("\"2.0.0\""));
if (path.endsWith(F(".dat"))) {
web_server.sendHeader(F("Content-Disposition"), F("attachment;"));
}
web_server.streamFile(dataFile, dataType);
dataFile.close();
// Search flash file system first, then SD if present
f = tryOpenFile(path.c_str(), "r");
#ifdef FEATURE_SD
if (!f) {
f = SD.open(path.c_str(), "r");
}
else
{
#ifdef FEATURE_SD
File dataFile = SD.open(path.c_str());
#endif // ifdef FEATURE_SD
if (!dataFile) {
return false;
}
if (path.endsWith(F(".DAT"))) {
web_server.sendHeader(F("Content-Disposition"), F("attachment;"));
}
web_server.streamFile(dataFile, dataType);
dataFile.close();
#else // ifdef FEATURE_SD
// File from SD requested, but no SD support.
if (!f) {
return false;
#endif // ifdef FEATURE_SD
}
// prevent reloading stuff on every click
web_server.sendHeader(F("Cache-Control"), F("max-age=3600, public"));
web_server.sendHeader(F("Vary"), "*");
web_server.sendHeader(F("ETag"), F("\"2.0.0\""));
if (path.endsWith(F(".dat"))) {
web_server.sendHeader(F("Content-Disposition"), F("attachment;"));
}
if (gzipEncoded(path)) {
web_server.sendHeader(F("Content-Encoding"), F("gzip"));
}
web_server.streamFile(f, dataType);
f.close();
statusLED(true);
return true;
}
size_t streamFromFS(String path, bool htmlEscape) {
// path is a deepcopy, since it will be changed here.
path = fileFromUrl(path);
statusLED(true);
size_t bytesStreamed = 0;
fs::File f;
// Search flash file system first, then SD if present
f = tryOpenFile(path.c_str(), "r");
#ifdef FEATURE_SD
if (!f) {
f = SD.open(path.c_str(), "r");
}
#endif // ifdef FEATURE_SD
if (!f) {
return bytesStreamed;
}
int available = f.available();
String escaped;
while (available > 0) {
uint32_t chunksize = 64;
if (available < chunksize) {
chunksize = available;
}
uint8_t buf[64] = {0};
const size_t read = f.read(buf, chunksize);
if (read == chunksize) {
for (uint32_t i = 0; i < chunksize; ++i) {
const char c = (char)buf[i];
if (htmlEscape && htmlEscapeChar(c, escaped)) {
addHtml(escaped);
} else {
addHtml(c);
}
}
bytesStreamed += read;
available = f.available();
} else {
available = 0;
}
}
statusLED(true);
f.close();
return bytesStreamed;
}
+6 -1
View File
@@ -4,6 +4,11 @@
#include "../WebServer/common.h"
bool loadFromFS(boolean spiffs, String path);
bool loadFromFS(String path);
// Send the content of a file directly to the webserver, like addHtml()
// Return is nr bytes streamed.
size_t streamFromFS(String path, bool htmlEscape = false);
#endif
+2 -9
View File
@@ -81,15 +81,8 @@ void handle_root() {
navMenuIndex = 0;
// if index.htm exists on FS serve that one (first check if gziped version exists)
if (loadFromFS(true, F("/index.htm.gz"))) { return; }
#ifdef FEATURE_SD
if (loadFromFS(false, F("/index.htm.gz"))) { return; }
#endif
if (loadFromFS(true, F("/index.htm"))) { return; }
#ifdef FEATURE_SD
if (loadFromFS(false, F("/index.htm"))) { return; }
#endif
if (loadFromFS(F("/index.htm.gz"))) { return; }
if (loadFromFS(F("/index.htm"))) { return; }
TXBuffer.startStream();
+2 -1
View File
@@ -5,6 +5,7 @@
#include "../WebServer/WebServer.h"
#include "../WebServer/AccessControl.h"
#include "../WebServer/HTML_wrappers.h"
#include "../WebServer/LoadFromFS.h"
#include "../WebServer/Markup.h"
#include "../WebServer/Markup_Buttons.h"
#include "../WebServer/Markup_Forms.h"
@@ -557,7 +558,7 @@ void Rule_showRuleTextArea(const String& fileName) {
size_t size = 0;
addHtml(F("<textarea id='rules' name='rules' rows='30' wrap='off'>"));
size = streamFile_htmlEscape(fileName);
size = streamFromFS(fileName, true);
addHtml(F("</textarea>"));
html_TR_TD();
+12 -15
View File
@@ -167,7 +167,7 @@ void handle_sysinfo_json() {
json_open(false, F("storage"));
# if defined(ESP8266)
uint32_t flashChipId = ESP.getFlashChipId();
uint32_t flashChipId = getFlashChipId();
// Set to HEX may be something like 0x1640E0.
// Where manufacturer is 0xE0 and device is 0x4016.
@@ -205,8 +205,8 @@ void handle_sysinfo_json() {
json_number(F("writes"), String(RTC.flashDayCounter));
json_number(F("flash_counter"), String(RTC.flashCounter));
json_number(F("sketch_size"), String(ESP.getSketchSize() / 1024));
json_number(F("sketch_free"), String(ESP.getFreeSketchSpace() / 1024));
json_number(F("sketch_size"), String(getSketchSize() / 1024));
json_number(F("sketch_free"), String(getFreeSketchSpace() / 1024));
json_number(F("spiffs_size"), String(SpiffsTotalBytes() / 1024));
json_number(F("spiffs_free"), String(SpiffsFreeSpace() / 1024));
@@ -612,8 +612,8 @@ void handle_sysinfo_Storage() {
uint32_t flashDevice = (flashChipId & 0xFF00) | ((flashChipId >> 16) & 0xFF);
addHtml(formatToHex(flashDevice));
}
uint32_t realSize = getFlashRealSizeInBytes();
uint32_t ideSize = ESP.getFlashChipSize();
const uint32_t realSize = getFlashRealSizeInBytes();
const uint32_t ideSize = ESP.getFlashChipSize();
addRowLabel(LabelType::FLASH_CHIP_REAL_SIZE);
addHtmlInt(realSize / 1024);
@@ -632,17 +632,14 @@ void handle_sysinfo_Storage() {
FlashMode_t ideMode = ESP.getFlashChipMode();
addRowLabel(LabelType::FLASH_IDE_MODE);
{
String html;
switch (ideMode) {
case FM_QIO: html += F("QIO"); break;
case FM_QOUT: html += F("QOUT"); break;
case FM_DIO: html += F("DIO"); break;
case FM_DOUT: html += F("DOUT"); break;
case FM_QIO: addHtml(F("QIO")); break;
case FM_QOUT: addHtml(F("QOUT")); break;
case FM_DIO: addHtml(F("DIO")); break;
case FM_DOUT: addHtml(F("DOUT")); break;
default:
html += getUnknownString(); break;
addHtml(getUnknownString()); break;
}
addHtml(html);
}
# endif // if defined(ESP8266)
@@ -663,9 +660,9 @@ void handle_sysinfo_Storage() {
{
String html;
html.reserve(32);
html += ESP.getSketchSize() / 1024;
html += getSketchSize() / 1024;
html += F(" kB (");
html += ESP.getFreeSketchSpace() / 1024;
html += getFreeSketchSpace() / 1024;
html += F(" kB free)");
addHtml(html);
}
+17 -35
View File
@@ -235,32 +235,6 @@ void sendHeadandTail_stdtemplate(boolean Tail, boolean rebooting) {
}
}
size_t streamFile_htmlEscape(const String& fileName)
{
fs::File f = tryOpenFile(fileName, "r");
size_t size = 0;
if (f)
{
while (f.available())
{
char c = (char)f.read();
String escaped;
if (htmlEscapeChar(c, escaped)) {
addHtml(escaped);
} else {
addHtml(c);
}
++size;
}
f.close();
}
return size;
}
bool captivePortal() {
const bool fromAP = web_server.client().localIP() == apIP;
const bool hasWiFiCredentials = SecuritySettings.hasWiFiCredentials();
@@ -1039,11 +1013,21 @@ String getControllerSymbol(uint8_t index)
return ret;
}
*/
void addSVG_param(const String& key, float value) {
void addSVG_param(const __FlashStringHelper * key, int value) {
addHtml(' ');
addHtml(key);
addHtml('=');
addHtml('\"');
addHtmlInt(value);
addHtml('\"');
}
void addSVG_param(const __FlashStringHelper * key, float value) {
addSVG_param(key, String(value, 2));
}
void addSVG_param(const String& key, const String& value) {
void addSVG_param(const __FlashStringHelper * key, const String& value) {
addHtml(' ');
addHtml(key);
addHtml('=');
@@ -1076,8 +1060,8 @@ void createSvgRect(const String& classname,
addSVG_param(F("stroke"), formatToHex(strokeColor, F("#")));
addSVG_param(F("stroke-width"), strokeWidth);
}
addSVG_param("x", xoffset);
addSVG_param("y", yoffset);
addSVG_param(F("x"), xoffset);
addSVG_param(F("y"), yoffset);
addSVG_param(F("width"), width);
addSVG_param(F("height"), height);
addSVG_param(F("rx"), rx);
@@ -1121,10 +1105,6 @@ void createSvgTextElement(const String& text, float textXoffset, float textYoffs
#define SVG_BAR_HEIGHT 16
#define SVG_BAR_WIDTH 400
void write_SVG_image_header(int width, int height) {
write_SVG_image_header(width, height, false);
}
void write_SVG_image_header(int width, int height, bool useViewbox) {
addHtml(F("<svg xmlns=\"http://www.w3.org/2000/svg\" width=\""));
addHtmlInt(width);
@@ -1259,7 +1239,9 @@ void getStorageTableSVG(SettingsType::Enum settingsType) {
float textYoffset = yOffset + 0.9f * SVG_BAR_HEIGHT;
if (struct_size != 0) {
String text = formatHumanReadable(struct_size, 1024);
String text;
text.reserve(32);
text = formatHumanReadable(struct_size, 1024);
text += '/';
text += formatHumanReadable(max_size, 1024);
text += F(" per item");
+6 -7
View File
@@ -40,7 +40,6 @@ void sendHeadandTail(const String& tmplName,
void sendHeadandTail_stdtemplate(boolean Tail = false,
boolean rebooting = false);
size_t streamFile_htmlEscape(const String& fileName);
void WebServerInit();
@@ -143,10 +142,13 @@ String getControllerSymbol(uint8_t index);
/*
String getValueSymbol(uint8_t index);
*/
void addSVG_param(const String& key,
void addSVG_param(const __FlashStringHelper * key,
int value);
void addSVG_param(const __FlashStringHelper * key,
float value);
void addSVG_param(const String& key,
void addSVG_param(const __FlashStringHelper * key,
const String& value);
void createSvgRect_noStroke(const __FlashStringHelper * classname,
@@ -181,12 +183,9 @@ void createSvgTextElement(const String& text,
float textXoffset,
float textYoffset);
void write_SVG_image_header(int width,
int height);
void write_SVG_image_header(int width,
int height,
bool useViewbox);
bool useViewbox = false);
/*
void getESPeasyLogo(int width_pixels);