Working API commands
This commit is contained in:
parent
b0828a6280
commit
eb65eafbe0
@ -53,6 +53,7 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
|
||||
bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest);
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest);
|
||||
void updateFSInfo();
|
||||
void closeFile();
|
||||
|
||||
//hue.cpp
|
||||
void handleHue();
|
||||
@ -90,8 +91,8 @@ void handleIR();
|
||||
|
||||
void deserializeSegment(JsonObject elem, byte it);
|
||||
bool deserializeState(JsonObject root);
|
||||
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false);
|
||||
void serializeState(JsonObject root, bool forPreset = false);
|
||||
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset = false, bool segmentBounds = true);
|
||||
void serializeState(JsonObject root, bool forPreset = false, bool includeBri = true, bool segmentBounds = true);
|
||||
void serializeInfo(JsonObject root);
|
||||
void serveJson(AsyncWebServerRequest* request);
|
||||
bool serveLiveLeds(AsyncWebServerRequest* request, uint32_t wsClient = 0);
|
||||
|
105
wled00/file.cpp
105
wled00/file.cpp
@ -10,7 +10,7 @@
|
||||
#include "esp_spiffs.h" //FS info bare IDF function until FS wrapper is available for ESP32
|
||||
#endif
|
||||
|
||||
#define FS_BUFSIZE 512
|
||||
#define FS_BUFSIZE 256
|
||||
|
||||
//allow presets to be added until this percentage of FS space is used
|
||||
#define FS_QUOTA 75
|
||||
@ -28,9 +28,24 @@
|
||||
* The reason for it is that deleting the first preset would require special code to handle commas between it and the 2nd preset
|
||||
*/
|
||||
|
||||
// There are no consecutive spaces longer than this in the file, so if more space is required, findSpace() can return false immediately
|
||||
// Actual space may be lower
|
||||
uint16_t knownLargestSpace = UINT16_MAX;
|
||||
|
||||
File f;
|
||||
|
||||
//wrapper to find out how long closing takes
|
||||
void closeFile() {
|
||||
DEBUGFS_PRINT(F("Close -> "));
|
||||
uint32_t s = millis();
|
||||
f.close();
|
||||
DEBUGFS_PRINTF("took %d ms\n", millis() - s);
|
||||
doCloseFile = false;
|
||||
}
|
||||
|
||||
//find() that reads and buffers data from file stream in 256-byte blocks.
|
||||
//Significantly faster, f.find(key) can take SECONDS for multi-kB files
|
||||
bool bufferedFind(const char *target, File f) {
|
||||
bool bufferedFind(const char *target, bool fromStart = true) {
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINT("Find ");
|
||||
DEBUGFS_PRINTLN(target);
|
||||
@ -44,7 +59,7 @@ bool bufferedFind(const char *target, File f) {
|
||||
byte c;
|
||||
uint16_t bufsize = 0, count = 0;
|
||||
byte buf[FS_BUFSIZE];
|
||||
f.seek(0);
|
||||
if (fromStart) f.seek(0);
|
||||
|
||||
while (f.position() < f.size() -1) {
|
||||
bufsize = f.read(buf, FS_BUFSIZE);
|
||||
@ -68,18 +83,25 @@ bool bufferedFind(const char *target, File f) {
|
||||
}
|
||||
|
||||
//find empty spots in file stream in 256-byte blocks.
|
||||
bool bufferedFindSpace(uint16_t targetLen, File f) {
|
||||
bool bufferedFindSpace(uint16_t targetLen, bool fromStart = true) {
|
||||
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTF("Find %d spaces\n", targetLen);
|
||||
uint32_t s = millis();
|
||||
#endif
|
||||
|
||||
if (knownLargestSpace < targetLen) {
|
||||
DEBUGFS_PRINT(F("No match, KLS "));
|
||||
DEBUGFS_PRINTLN(knownLargestSpace);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f || !f.size()) return false;
|
||||
|
||||
uint16_t index = 0;
|
||||
uint16_t bufsize = 0, count = 0;
|
||||
byte buf[FS_BUFSIZE];
|
||||
f.seek(0);
|
||||
if (fromStart) f.seek(0);
|
||||
|
||||
while (f.position() < f.size() -1) {
|
||||
bufsize = f.read(buf, FS_BUFSIZE);
|
||||
@ -88,13 +110,20 @@ bool bufferedFindSpace(uint16_t targetLen, File f) {
|
||||
while (count < bufsize) {
|
||||
if(buf[count] == ' ') {
|
||||
if(++index >= targetLen) { // return true if space long enough
|
||||
if (fromStart) {
|
||||
f.seek((f.position() - bufsize) + count +1 - targetLen);
|
||||
knownLargestSpace = UINT16_MAX; //there may be larger spaces after, so we don't know
|
||||
}
|
||||
DEBUGFS_PRINTF("Found at pos %d, took %d ms", f.position(), millis() - s);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (!fromStart) return false;
|
||||
if (index) {
|
||||
if (knownLargestSpace < index || knownLargestSpace == UINT16_MAX) knownLargestSpace = index;
|
||||
index = 0; // reset index if not space
|
||||
}
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
@ -104,7 +133,7 @@ bool bufferedFindSpace(uint16_t targetLen, File f) {
|
||||
}
|
||||
|
||||
//find the closing bracket corresponding to the opening bracket at the file pos when calling this function
|
||||
bool bufferedFindObjectEnd(File f) {
|
||||
bool bufferedFindObjectEnd() {
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTLN(F("Find obj end"));
|
||||
uint32_t s = millis();
|
||||
@ -137,7 +166,7 @@ bool bufferedFindObjectEnd(File f) {
|
||||
}
|
||||
|
||||
//fills n bytes from current file pos with ' ' characters
|
||||
void writeSpace(File f, uint16_t l)
|
||||
void writeSpace(uint16_t l)
|
||||
{
|
||||
byte buf[FS_BUFSIZE];
|
||||
memset(buf, ' ', FS_BUFSIZE);
|
||||
@ -147,9 +176,11 @@ void writeSpace(File f, uint16_t l)
|
||||
f.write(buf, block);
|
||||
l -= block;
|
||||
}
|
||||
|
||||
if (knownLargestSpace < l) knownLargestSpace = l;
|
||||
}
|
||||
|
||||
bool appendObjectToFile(File f, const char* key, JsonDocument* content, uint32_t s)
|
||||
bool appendObjectToFile(const char* key, JsonDocument* content, uint32_t s, uint32_t contentLen = 0)
|
||||
{
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTLN(F("Append"));
|
||||
@ -165,18 +196,19 @@ bool appendObjectToFile(File f, const char* key, JsonDocument* content, uint32_t
|
||||
}
|
||||
|
||||
if (content->isNull()) {
|
||||
f.close();
|
||||
doCloseFile = true;
|
||||
return true; //nothing to append
|
||||
}
|
||||
|
||||
//if there is enough empty space in file, insert there instead of appending
|
||||
uint32_t contentLen = measureJson(*content);
|
||||
if (!contentLen) contentLen = measureJson(*content);
|
||||
DEBUGFS_PRINTF("CLen %d\n", contentLen);
|
||||
if (bufferedFindSpace(contentLen + strlen(key) + 1, f)) {
|
||||
if (bufferedFindSpace(contentLen + strlen(key) + 1)) {
|
||||
if (f.position() > 2) f.write(','); //add comma if not first object
|
||||
f.print(key);
|
||||
serializeJson(*content, f);
|
||||
DEBUGFS_PRINTF("Inserted, took %d ms (total %d)", millis() - s1, millis() - s);
|
||||
doCloseFile = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -184,7 +216,7 @@ bool appendObjectToFile(File f, const char* key, JsonDocument* content, uint32_t
|
||||
|
||||
if ((fsBytesUsed*100)/fsBytesTotal > FS_QUOTA) { //permitted space for presets exceeded
|
||||
errorFlag = ERR_FS_QUOTA;
|
||||
f.close();
|
||||
doCloseFile = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -195,7 +227,7 @@ bool appendObjectToFile(File f, const char* key, JsonDocument* content, uint32_t
|
||||
if (pos == 0) //not found
|
||||
{
|
||||
DEBUGFS_PRINTLN("not }");
|
||||
while (bufferedFind("}",f)) //find last closing bracket in JSON if not last char
|
||||
while (bufferedFind("}", false)) //find last closing bracket in JSON if not last char
|
||||
{
|
||||
pos = f.position();
|
||||
}
|
||||
@ -216,7 +248,7 @@ bool appendObjectToFile(File f, const char* key, JsonDocument* content, uint32_t
|
||||
serializeJson(*content, f);
|
||||
f.write('}');
|
||||
|
||||
f.close();
|
||||
doCloseFile = true;
|
||||
DEBUGFS_PRINTF("Appended, took %d ms (total %d)", millis() - s1, millis() - s);
|
||||
return true;
|
||||
}
|
||||
@ -238,43 +270,56 @@ bool writeObjectToFile(const char* file, const char* key, JsonDocument* content)
|
||||
#endif
|
||||
|
||||
uint32_t pos = 0;
|
||||
File f = WLED_FS.open(file, "r+");
|
||||
f = WLED_FS.open(file, "r+");
|
||||
if (!f && !WLED_FS.exists(file)) f = WLED_FS.open(file, "w+");
|
||||
if (!f) {
|
||||
DEBUGFS_PRINTLN(F("Failed to open!"));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bufferedFind(key, f)) //key does not exist in file
|
||||
if (!bufferedFind(key)) //key does not exist in file
|
||||
{
|
||||
return appendObjectToFile(f, key, content, s);
|
||||
return appendObjectToFile(key, content, s);
|
||||
}
|
||||
|
||||
//exists
|
||||
//an object with this key already exists, replace or delete it
|
||||
pos = f.position();
|
||||
//measure out end of old object
|
||||
bufferedFindObjectEnd(f);
|
||||
bufferedFindObjectEnd();
|
||||
uint32_t pos2 = f.position();
|
||||
|
||||
uint32_t oldLen = pos2 - pos;
|
||||
DEBUGFS_PRINTF("Old obj len %d\n", oldLen);
|
||||
|
||||
if (!content->isNull() && measureJson(*content) <= oldLen) //replace
|
||||
{
|
||||
//Three cases:
|
||||
//1. The new content is null, overwrite old obj with spaces
|
||||
//2. The new content is smaller than the old, overwrite and fill diff with spaces
|
||||
//3. The new content is larger than the old, but smaller than old + trailing spaces, overwrite with new
|
||||
//4. The new content is larger than old + trailing spaces, delete old and append
|
||||
|
||||
uint32_t contentLen = 0;
|
||||
if (!content->isNull()) contentLen = measureJson(*content);
|
||||
|
||||
if (contentLen && contentLen <= oldLen) { //replace and fill diff with spaces
|
||||
DEBUGFS_PRINTLN(F("replace"));
|
||||
f.seek(pos);
|
||||
serializeJson(*content, f);
|
||||
writeSpace(f, pos2 - f.position());
|
||||
} else { //delete
|
||||
writeSpace(pos2 - f.position());
|
||||
} else if (contentLen && bufferedFindSpace(contentLen - oldLen, false)) { //enough leading spaces to replace
|
||||
DEBUGFS_PRINTLN(F("replace (trailing)"));
|
||||
f.seek(pos);
|
||||
serializeJson(*content, f);
|
||||
} else {
|
||||
DEBUGFS_PRINTLN(F("delete"));
|
||||
pos -= strlen(key);
|
||||
if (pos > 3) pos--; //also delete leading comma if not first object
|
||||
f.seek(pos);
|
||||
writeSpace(f, pos2 - pos);
|
||||
if (!content->isNull()) return appendObjectToFile(f, key, content, s);
|
||||
writeSpace(pos2 - pos);
|
||||
if (contentLen) return appendObjectToFile(key, content, s, contentLen);
|
||||
}
|
||||
f.close();
|
||||
DEBUGFS_PRINTF("Deleted, took %d ms\n", millis() - s);
|
||||
|
||||
doCloseFile = true;
|
||||
DEBUGFS_PRINTF("Replaced/deleted, took %d ms\n", millis() - s);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -287,16 +332,18 @@ bool readObjectFromFileUsingId(const char* file, uint16_t id, JsonDocument* dest
|
||||
|
||||
bool readObjectFromFile(const char* file, const char* key, JsonDocument* dest)
|
||||
{
|
||||
if (doCloseFile) closeFile();
|
||||
#ifdef WLED_DEBUG_FS
|
||||
DEBUGFS_PRINTF("Read from %s with key %s >>>\n", file, key);
|
||||
uint32_t s = millis();
|
||||
#endif
|
||||
File f = WLED_FS.open(file, "r");
|
||||
f = WLED_FS.open(file, "r");
|
||||
if (!f) return false;
|
||||
|
||||
if (!bufferedFind(key, f)) //key does not exist in file
|
||||
if (!bufferedFind(key)) //key does not exist in file
|
||||
{
|
||||
f.close();
|
||||
dest->clear();
|
||||
DEBUGFS_PRINTLN(F("Obj not found."));
|
||||
return false;
|
||||
}
|
||||
|
@ -260,7 +260,7 @@ bool deserializeState(JsonObject root)
|
||||
return stateResponse;
|
||||
}
|
||||
|
||||
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset)
|
||||
void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool forPreset, bool segmentBounds)
|
||||
{
|
||||
root[F("id")] = id;
|
||||
root[F("start")] = seg.start;
|
||||
@ -302,15 +302,16 @@ void serializeSegment(JsonObject& root, WS2812FX::Segment& seg, byte id, bool fo
|
||||
root[F("mi")] = seg.getOption(SEG_OPTION_MIRROR);
|
||||
}
|
||||
|
||||
void serializeState(JsonObject root, bool forPreset)
|
||||
void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segmentBounds)
|
||||
{
|
||||
if (errorFlag) root[F("error")] = errorFlag;
|
||||
if (includeBri) {
|
||||
root["on"] = (bri > 0);
|
||||
root["bri"] = briLast;
|
||||
root[F("transition")] = transitionDelay/100; //in 100ms
|
||||
}
|
||||
|
||||
if (!forPreset) {
|
||||
if (errorFlag) root["error"] = errorFlag;
|
||||
if (errorFlag) root[F("error")] = errorFlag;
|
||||
|
||||
root[F("ps")] = currentPreset;
|
||||
root[F("pss")] = savedPresets;
|
||||
@ -347,7 +348,7 @@ void serializeState(JsonObject root, bool forPreset)
|
||||
if (sg.isActive())
|
||||
{
|
||||
JsonObject seg0 = seg.createNestedObject();
|
||||
serializeSegment(seg0, sg, s, forPreset);
|
||||
serializeSegment(seg0, sg, s, forPreset, segmentBounds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,6 +70,10 @@ void WLED::loop()
|
||||
|
||||
if (doReboot)
|
||||
reset();
|
||||
if (doCloseFile) {
|
||||
closeFile();
|
||||
yield();
|
||||
}
|
||||
|
||||
if (!realtimeMode || realtimeOverride) // block stuff if WARLS/Adalight is enabled
|
||||
{
|
||||
|
@ -478,6 +478,7 @@ WLED_GLOBAL size_t fsBytesUsed _INIT(0);
|
||||
WLED_GLOBAL size_t fsBytesTotal _INIT(0);
|
||||
WLED_GLOBAL unsigned long presetsModifiedTime _INIT(0L);
|
||||
WLED_GLOBAL JsonDocument* fileDoc;
|
||||
WLED_GLOBAL bool doCloseFile _INIT(false);
|
||||
|
||||
// presets
|
||||
WLED_GLOBAL uint16_t savedPresets _INIT(0);
|
||||
|
@ -714,10 +714,13 @@ void savePreset(byte index, bool persist, const char* pname, JsonObject saveobj)
|
||||
|
||||
if (!sObj["o"]) {
|
||||
DEBUGFS_PRINTLN(F("Save current state"));
|
||||
serializeState(sObj, true);
|
||||
serializeState(sObj, true, sObj["ib"], sObj["sb"]);
|
||||
currentPreset = index;
|
||||
}
|
||||
sObj.remove("o");
|
||||
sObj.remove("ib");
|
||||
sObj.remove("sb");
|
||||
sObj.remove(F("error"));
|
||||
|
||||
writeObjectToFileUsingId("/presets.json", index, fileDoc);
|
||||
if (!docAlloc) delete fileDoc;
|
||||
|
Loading…
Reference in New Issue
Block a user