reworked String Handling of Enums

This commit is contained in:
2025-08-24 14:10:27 +02:00
parent f735ea7b0d
commit 9cb3a61184
8 changed files with 214 additions and 104 deletions

View File

@@ -337,89 +337,145 @@ void WebserverFirmwareUpdate_Callback(AsyncWebServerRequest *request, const Stri
}
}
}
/**
* @brief Callback function for handling EEPROM restore via the web server.
*
* This function is invoked during the EEPROM restore process when a new EEPROM file
* is received. It handles the restore process by reading the data from the received file,
* deserializing the JSON data, and updating the configuration and persistence data accordingly.
* If the restore is successful, it triggers a system shutdown.
*
* @param request Pointer to the AsyncWebServerRequest object.
* @param filename The name of the file being restored.
* @param index The index of the file being restored.
* @param data Pointer to the data buffer.
* @param len The length of the data buffer.
* @param final Boolean indicating if this is the final chunk of data.
*/
void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
void WebserverEERestore_Callback(AsyncWebServerRequest *request,
const String &filename,
size_t index,
uint8_t *data,
size_t len,
bool final)
{
constexpr size_t kBufCap = 1536;
bool ee_done = false;
static bool validext = false;
static char *buffer = NULL;
static char *buffer = nullptr;
static uint32_t read_ptr = 0;
DeserializationError error;
// kleines Helferlein zum sicheren Kopieren & Terminieren
auto safe_copy = [](char *dst, size_t dst_sz, const char *src)
{
if (!dst || dst_sz == 0)
return;
if (!src)
{
dst[0] = '\0';
return;
}
strncpy(dst, src, dst_sz - 1);
dst[dst_sz - 1] = '\0';
};
// Grenzen/Hilfen für Enum-Ranges (Sentinel bevorzugt, sonst *_Elements)
const int maxSpeedSrc = static_cast<int>(SPEEDSOURCE_COUNT);
const int maxGPSBaud = static_cast<int>(GPSBAUDRATE_COUNT);
const int maxCANSrc = static_cast<int>(CANSOURCE_COUNT);
if (!index)
{
validext = (filename.indexOf(".ee.json") > -1);
if (validext)
{
buffer = (char *)malloc(1536);
buffer = (char *)malloc(kBufCap);
read_ptr = 0;
if (buffer == NULL)
if (!buffer)
{
Debug_pushMessage("malloc() failed for EEPROM-Restore\n");
}
}
}
if (buffer != NULL && len > 0)
// Chunked receive mit Cap/Trunkierungsschutz
if (buffer && len > 0)
{
memcpy(buffer + read_ptr, data, len);
read_ptr = read_ptr + len;
size_t remain = (read_ptr < kBufCap) ? (kBufCap - read_ptr) : 0;
size_t to_copy = (len <= remain) ? len : remain;
if (to_copy > 0)
{
memcpy(buffer + read_ptr, data, to_copy);
read_ptr += to_copy;
}
else
{
Debug_pushMessage("EEPROM-Restore input exceeds buffer, truncating\n");
}
}
if (final)
{
if (buffer != NULL)
if (buffer)
{
// Ensure zero-termination just in case
if (read_ptr >= 1536)
read_ptr = 1535;
// Null-terminieren
if (read_ptr == kBufCap)
read_ptr = kBufCap - 1;
buffer[read_ptr] = '\0';
Serial.print(buffer);
JsonDocument json;
// Parse
JsonDocument json; // entspricht deinem bisherigen Stil
error = deserializeJson(json, buffer);
if (error)
{
Debug_pushMessage("deserializeJson() failed: %s\n", error.f_str());
}
else
else if (validext)
{
// ---- Konfiguration sicher in RAM übernehmen ----
// clamp-Helfer passend zu deinen Sanity-Grenzen
auto clamp_u32 = [](uint32_t v, uint32_t lo, uint32_t hi)
{ return (v < lo) ? lo : (v > hi ? hi : v); };
auto clamp_u16 = [](uint16_t v, uint16_t lo, uint16_t hi)
{ return (v < lo) ? lo : (v > hi ? hi : v); };
auto clamp_u8 = [](uint8_t v, uint8_t lo, uint8_t hi)
{ return (v < lo) ? lo : (v > hi ? hi : v); };
LubeConfig.DistancePerLube_Default = json["config"]["DistancePerLube_Default"].as<uint32_t>();
LubeConfig.DistancePerLube_Rain = json["config"]["DistancePerLube_Rain"].as<uint32_t>();
LubeConfig.tankCapacity_ml = json["config"]["tankCapacity_ml"].as<uint32_t>();
LubeConfig.amountPerDose_microL = json["config"]["amountPerDose_microL"].as<uint32_t>();
LubeConfig.TankRemindAtPercentage = json["config"]["TankRemindAtPercentage"].as<uint8_t>();
LubeConfig.PulsePerRevolution = json["config"]["PulsePerRevolution"].as<uint8_t>();
LubeConfig.TireWidth_mm = json["config"]["TireWidth_mm"].as<uint32_t>();
LubeConfig.TireWidthHeight_Ratio = json["config"]["TireWidthHeight_Ratio"].as<uint32_t>();
LubeConfig.RimDiameter_Inch = json["config"]["RimDiameter_Inch"].as<uint32_t>();
LubeConfig.DistancePerRevolution_mm = json["config"]["DistancePerRevolution_mm"].as<uint32_t>();
LubeConfig.BleedingPulses = json["config"]["BleedingPulses"].as<uint16_t>();
LubeConfig.SpeedSource = (SpeedSource_t)json["config"]["SpeedSource"].as<int>();
LubeConfig.GPSBaudRate = (GPSBaudRate_t)json["config"]["GPSBaudRate"].as<int>();
LubeConfig.CANSource = (CANSource_t)json["config"]["CANSource"].as<int>();
// config.*
LubeConfig.DistancePerLube_Default = clamp_u32(json["config"]["DistancePerLube_Default"].as<uint32_t>(), 0, 50000);
LubeConfig.DistancePerLube_Rain = clamp_u32(json["config"]["DistancePerLube_Rain"].as<uint32_t>(), 0, 50000);
LubeConfig.tankCapacity_ml = clamp_u32(json["config"]["tankCapacity_ml"].as<uint32_t>(), 0, 5000);
LubeConfig.amountPerDose_microL = clamp_u32(json["config"]["amountPerDose_microL"].as<uint32_t>(), 0, 100);
LubeConfig.TankRemindAtPercentage = clamp_u8(json["config"]["TankRemindAtPercentage"].as<uint8_t>(), 0, 100);
LubeConfig.PulsePerRevolution = clamp_u8(json["config"]["PulsePerRevolution"].as<uint8_t>(), 0, 255);
LubeConfig.TireWidth_mm = clamp_u32(json["config"]["TireWidth_mm"].as<uint32_t>(), 0, 500);
LubeConfig.TireWidthHeight_Ratio = clamp_u32(json["config"]["TireWidthHeight_Ratio"].as<uint32_t>(), 0, 150);
LubeConfig.RimDiameter_Inch = clamp_u32(json["config"]["RimDiameter_Inch"].as<uint32_t>(), 0, 30);
LubeConfig.DistancePerRevolution_mm = clamp_u32(json["config"]["DistancePerRevolution_mm"].as<uint32_t>(), 0, 10000);
LubeConfig.BleedingPulses = clamp_u16(json["config"]["BleedingPulses"].as<uint16_t>(), 0, 1000);
LubeConfig.WashMode_Distance = json["config"]["WashMode_Distance"].as<uint16_t>(); // ggf. Grenzen anpassen
LubeConfig.WashMode_Interval = json["config"]["WashMode_Interval"].as<uint16_t>(); // ggf. Grenzen anpassen
LubeConfig.LED_Mode_Flash = json["config"]["LED_Mode_Flash"].as<bool>();
LubeConfig.LED_Max_Brightness = json["config"]["LED_Max_Brightness"].as<uint8_t>();
LubeConfig.LED_Min_Brightness = json["config"]["LED_Min_Brightness"].as<uint8_t>();
strncpy(LubeConfig.wifi_ap_ssid, json["config"]["wifi_ap_ssid"].as<const char *>(), sizeof(LubeConfig.wifi_ap_ssid));
strncpy(LubeConfig.wifi_ap_password, json["config"]["wifi_ap_password"].as<const char *>(), sizeof(LubeConfig.wifi_ap_password));
strncpy(LubeConfig.wifi_client_ssid, json["config"]["wifi_client_ssid"].as<const char *>(), sizeof(LubeConfig.wifi_client_ssid));
strncpy(LubeConfig.wifi_client_password, json["config"]["wifi_client_password"].as<const char *>(), sizeof(LubeConfig.wifi_client_password));
// Enums nur nach Range-Check übernehmen
{
int v = json["config"]["SpeedSource"].as<int>();
if (v >= 0 && v < maxSpeedSrc)
LubeConfig.SpeedSource = (SpeedSource_t)v;
else
Debug_pushMessage("Restore: invalid SpeedSource=%d\n", v);
}
{
int v = json["config"]["GPSBaudRate"].as<int>();
if (v >= 0 && v < maxGPSBaud)
LubeConfig.GPSBaudRate = (GPSBaudRate_t)v;
else
Debug_pushMessage("Restore: invalid GPSBaudRate=%d\n", v);
}
{
int v = json["config"]["CANSource"].as<int>();
if (v >= 0 && v < maxCANSrc)
LubeConfig.CANSource = (CANSource_t)v;
else
Debug_pushMessage("Restore: invalid CANSource=%d\n", v);
}
// Strings sicher kopieren (0-terminiert)
safe_copy(LubeConfig.wifi_ap_ssid, sizeof(LubeConfig.wifi_ap_ssid), json["config"]["wifi_ap_ssid"]);
safe_copy(LubeConfig.wifi_ap_password, sizeof(LubeConfig.wifi_ap_password), json["config"]["wifi_ap_password"]);
safe_copy(LubeConfig.wifi_client_ssid, sizeof(LubeConfig.wifi_client_ssid), json["config"]["wifi_client_ssid"]);
safe_copy(LubeConfig.wifi_client_password, sizeof(LubeConfig.wifi_client_password), json["config"]["wifi_client_password"]);
// persis.*
PersistenceData.writeCycleCounter = json["persis"]["writeCycleCounter"].as<uint16_t>();
PersistenceData.tankRemain_microL = json["persis"]["tankRemain_microL"].as<uint32_t>();
PersistenceData.TravelDistance_highRes_mm = json["persis"]["TravelDistance_highRes_mm"].as<uint32_t>();
@@ -427,24 +483,33 @@ void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &f
PersistenceData.odometer = json["persis"]["odometer"].as<uint32_t>();
PersistenceData.checksum = json["persis"]["checksum"].as<uint32_t>();
// Optional: Sanity-Autokorrektur im RAM (keine EEPROM-Writes hier!)
{
uint32_t sanity = ConfigSanityCheck(true);
if (sanity > 0)
{
MaintainDTC(DTC_EEPROM_CFG_SANITY, true, sanity);
Debug_pushMessage("Restore: ConfigSanity corrected (mask=0x%08lX)\n", sanity);
}
}
ee_done = true;
}
}
if (buffer)
{
free(buffer);
buffer = NULL;
buffer = nullptr;
}
AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots");
// Browser zurückleiten & ggf. Shutdown
AsyncWebServerResponse *response =
request->beginResponse(302, "text/plain", "Please wait while the device reboots");
response->addHeader("Refresh", "20");
response->addHeader("Location", "/");
request->send(response);
if (ee_done)
{
Debug_pushMessage("Update complete\n");
Debug_pushMessage("EEPROM restore complete\n");
globals.systemStatus = sysStat_Shutdown;
}
}
@@ -648,24 +713,24 @@ void Websocket_HandleSettings(uint8_t *data)
}
else if (strcmp(identifier, "speedsource") == 0)
{
int index = findIndexByString(value, SpeedSourceString, (int)SpeedSourceString_Elements);
if (validIndex(index, (int)SpeedSourceString_Elements))
int index = findIndexByString(value, SpeedSourceString, (int)SPEEDSOURCE_COUNT);
if (validIndex(index, (int)SPEEDSOURCE_COUNT))
speedsourcePreselect = (SpeedSource_t)index;
else
Debug_pushMessage("Invalid speedsource '%s'\n", value);
}
else if (strcmp(identifier, "cansource") == 0)
{
int index = findIndexByString(value, CANSourceString, (int)CANSourceString_Elements);
if (validIndex(index, (int)CANSourceString_Elements))
int index = findIndexByString(value, CANSourceString, (int)CANSOURCE_COUNT);
if (validIndex(index, (int)CANSOURCE_COUNT))
LubeConfig.CANSource = (CANSource_t)index;
else
Debug_pushMessage("Invalid cansource '%s'\n", value);
}
else if (strcmp(identifier, "gpsbaud") == 0)
{
int index = findIndexByString(value, GPSBaudRateString, (int)GPSBaudRateString_Elements);
if (validIndex(index, (int)GPSBaudRateString_Elements))
int index = findIndexByString(value, GPSBaudRateString, (int)GPSBAUDRATE_COUNT);
if (validIndex(index, (int)GPSBAUDRATE_COUNT))
LubeConfig.GPSBaudRate = (GPSBaudRate_t)index;
else
Debug_pushMessage("Invalid gpsbaud '%s'\n", value);
@@ -801,7 +866,7 @@ void Websocket_RefreshClientData_Status(uint32_t client_id, bool send_mapping)
String temp = "STATUS:";
temp.concat(String(nz(globals.systemStatustxt)) + ";");
temp.concat(String(ToString(globals.systemStatus)) + ";");
// Guard against division by zero (capacity==0)
uint32_t cap = LubeConfig.tankCapacity_ml;
@@ -856,26 +921,26 @@ void Websocket_RefreshClientData_Static(uint32_t client_id, bool send_mapping)
temp += String(LubeConfig.RimDiameter_Inch) + ";";
// speedsource + Optionen
temp += tableStr(SpeedSourceString, (int)LubeConfig.SpeedSource, (int)SpeedSourceString_Elements) + ";";
temp += String(ToString(LubeConfig.SpeedSource)) + ";";
{
String csv;
appendCsv(csv, SpeedSourceString, SpeedSourceString_Elements);
appendCsv(csv, SpeedSourceString, SPEEDSOURCE_COUNT);
temp += csv + ";";
}
// gpsbaud + Optionen
temp += tableStr(GPSBaudRateString, (int)LubeConfig.GPSBaudRate, (int)GPSBaudRateString_Elements) + ";";
temp += String(ToString(LubeConfig.GPSBaudRate)) + ";";
{
String csv;
appendCsv(csv, GPSBaudRateString, GPSBaudRateString_Elements);
appendCsv(csv, GPSBaudRateString, GPSBAUDRATE_COUNT);
temp += csv + ";";
}
// cansource + Optionen
temp += tableStr(CANSourceString, (int)LubeConfig.CANSource, (int)CANSourceString_Elements) + ";";
temp += String(ToString(LubeConfig.CANSource)) + ";";
{
String csv;
appendCsv(csv, CANSourceString, CANSourceString_Elements);
appendCsv(csv, CANSourceString, CANSOURCE_COUNT);
temp += csv + ";";
}