api: optimize status endpoint payload and caching
This commit is contained in:
@@ -1103,6 +1103,45 @@ float calculateRuntimeHours() {
|
||||
return bmsData.remainingAh / dischargeAmps;
|
||||
}
|
||||
|
||||
|
||||
const int STATUS_FIELD_COUNT = 8;
|
||||
const char* STATUS_FIELDS[STATUS_FIELD_COUNT] = {
|
||||
"battery", "temps", "relays", "vehicle",
|
||||
"network", "alarms", "system", "config"
|
||||
};
|
||||
|
||||
int findStatusFieldIndex(const String& field) {
|
||||
for (int i = 0; i < STATUS_FIELD_COUNT; i++) {
|
||||
if (field == STATUS_FIELDS[i]) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t fnv1a32(const String& value) {
|
||||
uint32_t hash = 2166136261UL;
|
||||
for (unsigned int i = 0; i < value.length(); i++) {
|
||||
hash ^= static_cast<uint8_t>(value[i]);
|
||||
hash *= 16777619UL;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
String statusEtagForPayload(const String& payload) {
|
||||
return String("\"") + String(fnv1a32(payload), HEX) + "-" + String(payload.length(), HEX) + "\"";
|
||||
}
|
||||
|
||||
String lastStatusValidatorEtag = "";
|
||||
String lastStatusModified = "Thu, 01 Jan 1970 00:00:00 GMT";
|
||||
|
||||
String formatHttpDateFromUptime(unsigned long seconds) {
|
||||
char buffer[32];
|
||||
snprintf(buffer, sizeof(buffer), "Thu, 01 Jan 1970 %02lu:%02lu:%02lu GMT",
|
||||
(seconds / 3600UL) % 24UL,
|
||||
(seconds / 60UL) % 60UL,
|
||||
seconds % 60UL);
|
||||
return String(buffer);
|
||||
}
|
||||
|
||||
void buildStatusDocument(JsonDocument& doc) {
|
||||
doc["type"] = MSG_STATUS_RESPONSE;
|
||||
doc["timestamp"] = millis();
|
||||
@@ -2640,9 +2679,75 @@ void handleStatus() {
|
||||
DynamicJsonDocument doc(4096);
|
||||
buildStatusDocument(doc);
|
||||
|
||||
if (server.hasArg("fields")) {
|
||||
String fields = server.arg("fields");
|
||||
DynamicJsonDocument filtered(4096);
|
||||
filtered["type"] = doc["type"];
|
||||
filtered["timestamp"] = doc["timestamp"];
|
||||
|
||||
int start = 0;
|
||||
while (start <= fields.length()) {
|
||||
int comma = fields.indexOf(',', start);
|
||||
if (comma < 0) comma = fields.length();
|
||||
|
||||
String field = fields.substring(start, comma);
|
||||
field.trim();
|
||||
|
||||
int fieldIndex = findStatusFieldIndex(field);
|
||||
if (fieldIndex < 0) {
|
||||
DynamicJsonDocument errorDoc(512);
|
||||
errorDoc["ok"] = false;
|
||||
errorDoc["error"] = "invalid_field";
|
||||
JsonArray details = errorDoc.createNestedArray("details");
|
||||
details.add(String("Unknown field '") + field + "'");
|
||||
|
||||
String errorOutput;
|
||||
serializeJson(errorDoc, errorOutput);
|
||||
server.send(400, "application/json", errorOutput);
|
||||
return;
|
||||
}
|
||||
|
||||
filtered[field] = doc[field];
|
||||
|
||||
start = comma + 1;
|
||||
if (comma == fields.length()) break;
|
||||
}
|
||||
|
||||
doc.clear();
|
||||
doc.set(filtered);
|
||||
}
|
||||
|
||||
String output;
|
||||
serializeJson(doc, output);
|
||||
|
||||
DynamicJsonDocument validatorDoc(4096);
|
||||
deserializeJson(validatorDoc, output);
|
||||
validatorDoc["timestamp"] = 0;
|
||||
if (validatorDoc["system"].is<JsonObject>()) {
|
||||
validatorDoc["system"]["uptime_seconds"] = 0;
|
||||
}
|
||||
|
||||
String validatorPayload;
|
||||
serializeJson(validatorDoc, validatorPayload);
|
||||
|
||||
String etag = statusEtagForPayload(validatorPayload);
|
||||
if (etag != lastStatusValidatorEtag) {
|
||||
lastStatusValidatorEtag = etag;
|
||||
lastStatusModified = formatHttpDateFromUptime(millis() / 1000);
|
||||
}
|
||||
|
||||
server.sendHeader("ETag", etag);
|
||||
server.sendHeader("Last-Modified", lastStatusModified);
|
||||
server.sendHeader("Cache-Control", "no-cache");
|
||||
|
||||
String ifNoneMatch = server.header("If-None-Match");
|
||||
String ifModifiedSince = server.header("If-Modified-Since");
|
||||
|
||||
if (ifNoneMatch == etag || ifModifiedSince == lastStatusModified) {
|
||||
server.send(304);
|
||||
return;
|
||||
}
|
||||
|
||||
server.send(200, "application/json", output);
|
||||
}
|
||||
|
||||
@@ -2747,6 +2852,9 @@ void setup() {
|
||||
server.on("/bms/select", HTTP_POST, handleSelectBms);
|
||||
|
||||
|
||||
const char* statusCacheHeaders[] = {"If-None-Match", "If-Modified-Since"};
|
||||
server.collectHeaders(statusCacheHeaders, 2);
|
||||
|
||||
server.begin();
|
||||
|
||||
Serial.println("Web Server Started");
|
||||
|
||||
Reference in New Issue
Block a user