530 lines
16 KiB
C++
Raw Normal View History

#include "webui.h"
AsyncWebServer webServer(80);
2023-04-13 00:32:50 +02:00
const char *PARAM_MESSAGE = "message";
String processor(const String &var);
void WebserverPOST_Callback(AsyncWebServerRequest *request);
void WebserverNotFound_Callback(AsyncWebServerRequest *request);
void Webserver_Callback(AsyncWebServerRequest *request);
void WebserverFirmwareUpdate_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);
void WebServerEEJSON_Callback(AsyncWebServerRequest *request);
void GetFlashVersion(char *buff, size_t buff_size);
2023-04-13 00:32:50 +02:00
AsyncWebSocket webSocket("/ws");
void WebsocketEvent_Callback(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
void Websocket_HandleMessage(void *arg, uint8_t *data, size_t len);
void initWebUI()
{
if (!LittleFS.begin())
{
2023-04-13 00:32:50 +02:00
Debug_pushMessage("An Error has occurred while mounting LittleFS\n");
MaintainDTC(DTC_FLASHFS_ERROR, DTC_CRITICAL, true);
return;
}
GetFlashVersion(globals.FlashVersion, sizeof(globals.FlashVersion));
2023-04-13 00:32:50 +02:00
char buffer[6];
snprintf(buffer, sizeof(buffer), "%d.%02d", constants.Required_Flash_Version_major, constants.Required_Flash_Version_minor);
if (strcmp(globals.FlashVersion, buffer))
{
MaintainDTC(DTC_FLASHFS_VERSION_ERROR, DTC_WARN, true);
}
MDNS.begin(globals.DeviceName);
MDNS.addService("http", "tcp", 80);
2023-04-13 00:32:50 +02:00
webSocket.onEvent(WebsocketEvent_Callback);
webServer.addHandler(&webSocket);
webServer.serveStatic("/static/", LittleFS, "/static/").setCacheControl("max-age=360000");
webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->redirect("/index.htm"); });
webServer.onNotFound(WebserverNotFound_Callback);
webServer.on("/index.htm", HTTP_GET, Webserver_Callback);
webServer.on("/post.htm", HTTP_POST, WebserverPOST_Callback);
webServer.on("/eejson", HTTP_GET, WebServerEEJSON_Callback);
webServer.on(
"/doUpdate", HTTP_POST, [](AsyncWebServerRequest *request) {}, WebserverFirmwareUpdate_Callback);
webServer.on(
"/eeRestore", HTTP_POST, [](AsyncWebServerRequest *request) {}, WebserverEERestore_Callback);
webServer.begin();
}
2023-04-13 00:32:50 +02:00
void Webserver_Process()
{
webSocket.cleanupClients();
}
String processor(const String &var)
{
2023-04-13 00:32:50 +02:00
if (var == "HOSTNAME")
return String(globals.DeviceName);
if (var == "SYSTEM_STATUS")
2023-04-17 21:53:52 +02:00
return String(sSystem_Status_txt[globals.systemStatus]);
2023-04-13 00:32:50 +02:00
if (var == "SW_VERSION")
2022-05-15 16:37:34 +02:00
{
2023-04-13 00:32:50 +02:00
char buffer[6];
snprintf(buffer, sizeof(buffer), "%d.%02d", constants.FW_Version_major, constants.FW_Version_minor);
return String(buffer);
2022-05-15 16:37:34 +02:00
}
2023-04-13 00:32:50 +02:00
if (var == "FS_VERSION")
return String(globals.FlashVersion);
if (var == "GIT_REV")
return String(constants.GitHash);
2022-05-15 16:37:34 +02:00
2023-04-13 00:32:50 +02:00
if (var == "SHOW_DTC_TABLE")
return globals.systemStatus == sysStat_Error ? "" : "hidden";
if (var == "BAT_REMAIN_CAPACITY")
return String(globals.battery_level);
2023-04-13 00:32:50 +02:00
if (var == "DEVICENAME")
return String(globals.DeviceName);
2023-04-18 13:00:34 +02:00
if (var == "DEVICENAME_ID")
return String(globals.DeviceName_ID);
2023-04-13 00:32:50 +02:00
if (var == "BATTERY_TYPE")
return String(ConfigData.batteryType);
if (var == "BAT_VOLTAGE")
return String((float)globals.loadvoltage_mV / 1000.0);
2023-04-13 00:32:50 +02:00
if (var == "PERSISTANCE_CHECKSUM")
{
char buffer[7];
sprintf(buffer, "0x%04X", PersistenceData.checksum);
return String(buffer);
}
if (var == "WRITE_CYCLE_COUNT")
return String(PersistenceData.writeCycleCounter);
if (var == "PERSISTENCE_MARKER")
return String(globals.eePersistanceAdress);
if (var == "EEPROM_VERSION")
return String(ConfigData.EEPROM_Version);
if (var == "CONFIG_CHECKSUM")
{
char buffer[7];
sprintf(buffer, "0x%04X", ConfigData.checksum);
return String(buffer);
}
if (var == "DTC_TABLE")
{
2023-04-13 00:32:50 +02:00
String temp = "";
char buff_timestamp[16]; // Format: DD-hh:mm:ss:xxx
for (uint32_t i = 0; i < MAX_DTC_STORAGE; i++)
{
2023-04-13 00:32:50 +02:00
if (DTCStorage[i].Number < DTC_LAST_DTC)
{
sprintf(buff_timestamp, "%02d-%02d:%02d:%02d:%03d",
DTCStorage[i].timestamp / 86400000, // Days
DTCStorage[i].timestamp / 360000 % 24, // Hours
DTCStorage[i].timestamp / 60000 % 60, // Minutes
DTCStorage[i].timestamp / 1000 % 60, // Seconds
DTCStorage[i].timestamp % 1000); // milliseconds
2023-04-13 00:32:50 +02:00
temp = temp + "<tr data-dtc=" + String(DTCStorage[i].Number);
temp = temp + " data-debugval=" + String(DTCStorage[i].debugVal) + "><td>" + String(buff_timestamp);
temp = temp + "</td><td>" + String(DTCStorage[i].Number) + "</td><td>";
2023-04-13 00:32:50 +02:00
temp = temp + "<img src=static/img/";
switch (DTCStorage[i].severity)
{
case DTC_CRITICAL:
temp = temp + "critical";
break;
case DTC_WARN:
temp = temp + "warn";
break;
case DTC_INFO:
temp = temp + "info";
break;
}
temp = temp + ".png></td><td>";
if (DTCStorage[i].active == DTC_ACTIVE)
temp = temp + "active";
else if (DTCStorage[i].active == DTC_PREVIOUS)
temp = temp + "previous";
else
temp = temp + "none";
temp = temp + "</td></tr>";
}
}
return temp;
}
if (var == "PLACEHOLDER")
return "placeholder";
if (var == "POINTS_FAC_1")
{
char buff[12];
snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_1_timer / 3600, (PersistenceData.faction_1_timer / 60) % 60, PersistenceData.faction_1_timer % 60);
return String(buff);
}
if (var == "POINTS_FAC_2")
{
char buff[12];
snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_2_timer / 3600, (PersistenceData.faction_2_timer / 60) % 60, PersistenceData.faction_2_timer % 60);
return String(buff);
}
if (var == "POINTS_FAC_3")
{
char buff[12];
snprintf(buff, 12, "%3d:%02d:%02d", PersistenceData.faction_3_timer / 3600, (PersistenceData.faction_3_timer / 60) % 60, PersistenceData.faction_3_timer % 60);
return String(buff);
}
2023-04-13 01:28:41 +02:00
if (var == "ACTIVE_FACTION")
return String(PersistenceData.activeFaction);
2023-04-18 20:06:57 +02:00
if (var == "FACTION_1_ACTIVE")
return String(PersistenceData.activeFaction == FACTION_1 ? "bg-primary" : "bg-secondary");
if (var == "FACTION_2_ACTIVE")
return String(PersistenceData.activeFaction == FACTION_2 ? "bg-primary" : "bg-secondary");
if (var == "FACTION_3_ACTIVE")
return String(PersistenceData.activeFaction == FACTION_3 ? "bg-primary" : "bg-secondary");
if (var == "NAME_FAC_1")
2023-04-18 20:06:57 +02:00
return String(ConfigData.Faction_1_Name);
if (var == "NAME_FAC_2")
2023-04-18 20:06:57 +02:00
return String(ConfigData.Faction_2_Name);
if (var == "NAME_FAC_3")
2023-04-18 20:06:57 +02:00
return String(ConfigData.Faction_3_Name);
2023-04-13 00:32:50 +02:00
if (var == "BATTERY_SELECT_OPTIONS")
{
2023-04-13 00:32:50 +02:00
String temp;
for (uint32_t i = 0; i < BatteryString_Elements; i++)
{
String selected = ConfigData.batteryType == i ? " selected " : "";
temp = temp + "<option value=\"" + i + "\"" + selected + ">" + BatteryString[i] + "</option>";
}
return temp;
}
if (var == "FACTIONREBOOT_CHECKED")
return String(ConfigData.active_faction_on_reboot == true ? "checked" : "");
if (var == "FACTION_RECOVERY")
return String(ConfigData.active_faction_on_reboot);
return String();
}
void Webserver_Callback(AsyncWebServerRequest *request)
{
request->send(LittleFS, "/index.htm", "text/html", false, processor);
}
void WebserverPOST_Callback(AsyncWebServerRequest *request)
{
2023-04-13 00:32:50 +02:00
request->send(LittleFS, "/post.htm", "text/html", false, processor);
Debug_pushMessage("POST:\n");
int paramsNr = request->params();
for (int i = 0; i < paramsNr; i++)
{
AsyncWebParameter *p = request->getParam(i);
2023-04-13 00:32:50 +02:00
Debug_pushMessage("%s : %s\n", p->name().c_str(), p->value().c_str());
2023-04-13 01:28:41 +02:00
// begin: POST Form Maintenance
if (p->name() == "reset_ee_btn")
{
if (request->hasParam("reset_ee_pds", true))
{
AsyncWebParameter *param = request->getParam("reset_ee_pds", true);
if (param->value() == "on")
globals.requestEEAction = globals.requestEEAction == EE_CFG_FORMAT ? EE_FORMAT_ALL : EE_PDS_FORMAT;
}
if (request->hasParam("reset_ee_cfg", true))
{
AsyncWebParameter *param = request->getParam("reset_ee_cfg", true);
if (param->value() == "on")
globals.requestEEAction = globals.requestEEAction == EE_PDS_FORMAT ? EE_FORMAT_ALL : EE_CFG_FORMAT;
}
}
if (p->name() == "reboot")
{
globals.systemStatus = sysStat_Shutdown;
}
2023-04-17 21:51:13 +02:00
if (p->name() == "resetpoints")
{
PersistenceData.faction_1_timer = 0;
PersistenceData.faction_2_timer = 0;
PersistenceData.faction_3_timer = 0;
PersistenceData.activeFaction = NONE;
globals.requestEEAction == EE_PDS_SAVE;
}
2023-04-13 01:28:41 +02:00
// end: POST Form Maintenance
2023-04-13 00:32:50 +02:00
// begin: POST Form Settings
2023-04-13 01:28:41 +02:00
if (p->name() == "battery_select")
{
batteryType_t temp = (batteryType_t)p->value().toInt();
ConfigData.batteryType = temp;
}
if (request->hasParam("factionreboot_cont", true))
{
AsyncWebParameter *param = request->getParam("factionreboot_cont", true);
if (param->value() == "on")
ConfigData.active_faction_on_reboot = true;
2023-04-17 23:14:46 +02:00
}
else
{
ConfigData.active_faction_on_reboot = false;
}
2023-04-18 20:06:57 +02:00
if (p->name() == "faction_1_name")
{
strncpy(ConfigData.Faction_1_Name, p->value().c_str(), sizeof(ConfigData.Faction_1_Name));
}
if (p->name() == "faction_2_name")
{
strncpy(ConfigData.Faction_2_Name, p->value().c_str(), sizeof(ConfigData.Faction_2_Name));
}
if (p->name() == "faction_3_name")
{
strncpy(ConfigData.Faction_3_Name, p->value().c_str(), sizeof(ConfigData.Faction_3_Name));
}
2023-04-13 01:28:41 +02:00
if (p->name() == "settingssave")
2023-04-13 00:32:50 +02:00
globals.requestEEAction = EE_CFG_SAVE;
// end: POST Form Settings
}
}
void WebserverNotFound_Callback(AsyncWebServerRequest *request)
{
request->send(404, "text/html", "Not found");
2022-05-15 16:37:34 +02:00
}
void GetFlashVersion(char *buff, size_t buff_size)
{
File this_file = LittleFS.open("version", "r");
if (!this_file)
{ // failed to open the file, retrn empty result
buff[0] = '\0';
return;
}
if (this_file.available())
{
int bytes_read;
bytes_read = this_file.readBytesUntil('\r', buff, buff_size - 1);
buff[bytes_read] = '\0';
}
this_file.close();
}
void WebserverFirmwareUpdate_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
{
if (!index)
{
2023-04-13 00:32:50 +02:00
Debug_pushMessage("Update");
size_t content_len = request->contentLength();
int cmd = (filename.indexOf(".fs") > -1) ? U_FS : U_FLASH;
Update.runAsync(true);
if (!Update.begin(content_len, cmd))
{
Update.printError(Serial);
}
}
if (Update.write(data, len) != len)
{
Update.printError(Serial);
}
else
{
2023-04-13 00:32:50 +02:00
Debug_pushMessage("Progress: %d%%\n", (Update.progress() * 100) / Update.size());
}
if (final)
{
AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots");
response->addHeader("Refresh", "20");
response->addHeader("Location", "/");
request->send(response);
if (!Update.end(true))
{
Update.printError(Serial);
}
else
{
2023-04-13 00:32:50 +02:00
Debug_pushMessage("Update complete\n");
globals.systemStatus = sysStat_Shutdown;
}
}
}
void WebserverEERestore_Callback(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
{
bool ee_done = false;
2023-04-13 00:32:50 +02:00
static bool validext = false;
static char *buffer = NULL;
static uint32_t read_ptr = 0;
DeserializationError error;
if (!index)
{
validext = (filename.indexOf(".ee.json") > -1);
2023-04-13 00:32:50 +02:00
if (validext)
{
buffer = (char *)malloc(1536);
read_ptr = 0;
if (buffer == NULL)
Debug_pushMessage("malloc() failed for EEPROM-Restore");
}
}
2023-04-13 00:32:50 +02:00
if (buffer != NULL)
{
2023-04-13 00:32:50 +02:00
memcpy(buffer + read_ptr, data, len);
read_ptr = read_ptr + len;
}
if (final)
{
2023-04-13 00:32:50 +02:00
if (buffer != NULL)
{
Serial.print(buffer);
StaticJsonDocument<1536> doc;
error = deserializeJson(doc, buffer);
if (error)
{
Debug_pushMessage("deserializeJson() failed: %s\n", error.f_str());
}
else
{
ConfigData.batteryType = (batteryType_t)doc["config"]["batteryType"].as<uint32_t>();
ConfigData.EEPROM_Version = doc["config"]["EEPROM_Version"].as<uint32_t>();
PersistenceData.writeCycleCounter = doc["persis"]["writeCycleCounter"].as<uint16_t>();
PersistenceData.activeFaction = (factions_t)doc["persis"]["activeFaction"].as<uint32_t>();
PersistenceData.faction_1_timer = doc["persis"]["faction_1_timer"].as<uint32_t>();
PersistenceData.faction_2_timer = doc["persis"]["faction_2_timer"].as<uint32_t>();
PersistenceData.faction_3_timer = doc["persis"]["faction_3_timer"].as<uint32_t>();
PersistenceData.checksum = doc["persis"]["checksum"].as<uint32_t>();
ee_done = true;
}
}
free(buffer);
AsyncWebServerResponse *response = request->beginResponse(302, "text/plain", "Please wait while the device reboots");
response->addHeader("Refresh", "20");
response->addHeader("Location", "/");
request->send(response);
2023-04-13 00:32:50 +02:00
if (ee_done)
{
2023-04-13 00:32:50 +02:00
Debug_pushMessage("Update complete");
globals.systemStatus = sysStat_Shutdown;
}
else
{
}
}
}
void WebServerEEJSON_Callback(AsyncWebServerRequest *request)
{
AsyncResponseStream *response = request->beginResponseStream("application/json");
DynamicJsonDocument json(1024);
JsonObject fwinfo = json.createNestedObject("info");
char buffer[16];
fwinfo["DeviceName"] = globals.DeviceName;
2023-04-13 00:32:50 +02:00
sprintf(buffer, "%d.%02d", constants.Required_Flash_Version_major, constants.Required_Flash_Version_minor);
fwinfo["FW-Version"] = buffer;
fwinfo["FS-Version"] = globals.FlashVersion;
2023-04-13 00:32:50 +02:00
snprintf_P(buffer, sizeof(buffer), "%s", constants.GitHash);
fwinfo["Git-Hash"] = buffer;
JsonObject config = json.createNestedObject("config");
config["EEPROM_Version"] = ConfigData.EEPROM_Version;
2023-04-13 00:32:50 +02:00
config["batteryType"] = ConfigData.batteryType;
sprintf(buffer, "0x%08X", ConfigData.checksum);
config["checksum"] = buffer;
JsonObject eepart = json.createNestedObject("eepart");
sprintf(buffer, "0x%04X", globals.eePersistanceAdress);
eepart["PersistanceAddress"] = buffer;
JsonObject persis = json.createNestedObject("persis");
persis["writeCycleCounter"] = PersistenceData.writeCycleCounter;
2023-04-13 00:32:50 +02:00
persis["activeFaction"] = PersistenceData.activeFaction;
persis["faction_1_timer"] = PersistenceData.faction_1_timer;
persis["faction_2_timer"] = PersistenceData.faction_2_timer;
persis["faction_3_timer"] = PersistenceData.faction_3_timer;
sprintf(buffer, "0x%08X", PersistenceData.checksum);
persis["checksum"] = buffer;
serializeJsonPretty(json, *response);
response->addHeader("Content-disposition", "attachment; filename=backup.ee.json");
request->send(response);
2023-04-13 00:32:50 +02:00
}
void WebsocketEvent_Callback(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len)
{
switch (type)
{
case WS_EVT_CONNECT:
Debug_pushMessage("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
break;
case WS_EVT_DISCONNECT:
Debug_pushMessage("WebSocket client #%u disconnected\n", client->id());
break;
case WS_EVT_DATA:
Websocket_HandleMessage(arg, data, len);
break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
}
}
void Websocket_HandleMessage(void *arg, uint8_t *data, size_t len)
{
AwsFrameInfo *info = (AwsFrameInfo *)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT)
{
data[len] = 0;
Debug_pushMessage("Got WebSocket Message: %s \n", (char *)data);
if (strcmp((char *)data, "start") == 0)
{
SetDebugportStatus(dbg_Webui, enabled);
}
else if (strcmp((char *)data, "stop") == 0)
{
SetDebugportStatus(dbg_Webui, disabled);
}
else if (strcmp((char *)data, "foo") == 0)
{
Debug_pushMessage("Got WebSocket Message 'foo' from client\n");
}
}
}
void Websocket_PushLiveDebug(String Message)
{
webSocket.textAll(Message + "\n");
}