176 lines
5.7 KiB
C++
176 lines
5.7 KiB
C++
#include <webserver.h>
|
|
|
|
void on_ping(TimerHandle_t xTimer)
|
|
{
|
|
if (!xTimer)
|
|
return;
|
|
auto ws = (AsyncWebSocket *)pvTimerGetTimerID(xTimer);
|
|
ws->pingAll();
|
|
ws->cleanupClients();
|
|
}
|
|
|
|
AstroWebServer::AstroWebServer(const uint8_t port, fs::FS &filesystem) : m_port(port), m_webserver(AsyncWebServer(port)), m_websocket(AsyncWebSocket("/ws")), m_filesystem(filesystem)
|
|
{
|
|
LOG_DEBUG("Initializing Web Server");
|
|
m_websocket.onEvent([this](AsyncWebSocket *server, AsyncWebSocketClient *client,
|
|
AwsEventType type, void *arg, uint8_t *data, size_t len)
|
|
{ onWsEvent(server, client, type, arg, data, len); });
|
|
|
|
m_webserver.addHandler(&m_websocket);
|
|
m_webserver.serveStatic("/", m_filesystem, "/").setDefaultFile("index.html");
|
|
|
|
m_webserver.on("/upload", HTTP_POST, [this](AsyncWebServerRequest *request)
|
|
{ onUploadRequest(request); }, [this](AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
|
|
{ onUploadHandler(request, filename, index, data, len, final); });
|
|
|
|
m_webserver.begin();
|
|
m_websocket.enable(true);
|
|
|
|
m_pingTimer = xTimerCreate("wsPingTimer", pdMS_TO_TICKS(2000), pdTRUE, (void *)&m_websocket, on_ping);
|
|
|
|
registerWsCommand("setTime", [this](const ArduinoJson::JsonDocument &doc)
|
|
{ onSetTme(doc); });
|
|
|
|
LOG_DEBUG("Webserver Init OK");
|
|
}
|
|
|
|
AstroWebServer::~AstroWebServer()
|
|
{
|
|
xTimerDelete(m_pingTimer, pdMS_TO_TICKS(10));
|
|
m_webserver.removeHandler(&m_websocket);
|
|
m_webserver.end();
|
|
}
|
|
|
|
void AstroWebServer::sendWsData(const String &data)
|
|
{
|
|
if (m_websocket.count())
|
|
{
|
|
m_websocket.textAll(data);
|
|
}
|
|
}
|
|
|
|
void AstroWebServer::registerWsCommand(const std::string &cmd, const WScommand func)
|
|
{
|
|
if (cmd.empty() || m_webserverCommands.contains(cmd))
|
|
return;
|
|
if (!func)
|
|
return;
|
|
m_webserverCommands[cmd] = func;
|
|
}
|
|
|
|
void AstroWebServer::unRegisterWsCommand(const std::string &cmd)
|
|
{
|
|
if (m_webserverCommands.contains(cmd))
|
|
m_webserverCommands.erase(cmd);
|
|
}
|
|
|
|
void AstroWebServer::onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len)
|
|
{
|
|
switch (type)
|
|
{
|
|
case WS_EVT_CONNECT:
|
|
LOG_DEBUG("WS client IP[", client->remoteIP().toString().c_str(), "]-ID[", client->id(), "] CONNECTED");
|
|
break;
|
|
case WS_EVT_DISCONNECT:
|
|
LOG_DEBUG("WS client IP[", client->remoteIP().toString().c_str(), "]-ID[", client->id(), "] DISCONNECTED");
|
|
break;
|
|
case WS_EVT_PONG:
|
|
LOG_DEBUG("WS client IP[", client->remoteIP().toString().c_str(), "]-ID[", client->id(), "] PONG");
|
|
break;
|
|
case WS_EVT_DATA:
|
|
{
|
|
AwsFrameInfo *info = (AwsFrameInfo *)arg;
|
|
if (info->final && info->index == 0 && info->len == len)
|
|
{
|
|
std::string data_str((char *)data, len);
|
|
ArduinoJson::JsonDocument doc;
|
|
if (auto rv = ArduinoJson::deserializeJson(doc, data_str) != ArduinoJson::DeserializationError::Ok)
|
|
{
|
|
LOG_ERROR("WS Client unable to deserialize Json");
|
|
return;
|
|
}
|
|
if (!doc["cmd"].is<std::string>() || !m_webserverCommands.contains(doc["cmd"]))
|
|
{
|
|
LOG_WARN("WS Client Invalid Json command [", doc["cmd"].as<std::string>().c_str(), "]");
|
|
return;
|
|
}
|
|
// execute callback function
|
|
m_webserverCommands[doc["cmd"]](doc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AstroWebServer::onUploadRequest(AsyncWebServerRequest *request)
|
|
{
|
|
if (m_uploadFailed)
|
|
request->send(500, "text/plain", "Upload failed");
|
|
else
|
|
request->send(200, "text/plain", "Upload successful");
|
|
}
|
|
|
|
void AstroWebServer::onUploadHandler(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
|
|
{
|
|
if (index == 0) // only on first iteration to open file
|
|
{
|
|
m_uploadFailed = false;
|
|
String safeName = filename;
|
|
int slashIndex = safeName.lastIndexOf('/');
|
|
if (slashIndex >= 0)
|
|
safeName = safeName.substring(slashIndex + 1);
|
|
if (safeName.length() == 0)
|
|
{
|
|
m_uploadFailed = true;
|
|
LOG_ERROR("Invalid file name");
|
|
return;
|
|
}
|
|
|
|
const std::filesystem::path filePath = std::filesystem::path(m_filesystem.mountpoint()) / safeName.c_str();
|
|
if (m_filesystem.exists(filePath.c_str()))
|
|
m_filesystem.remove(filePath.c_str());
|
|
|
|
m_uploadFile = m_filesystem.open(filePath.c_str(), FILE_WRITE);
|
|
if (!m_uploadFile)
|
|
{
|
|
m_uploadFailed = true;
|
|
LOG_ERROR("Failed to open upload file:", filePath.c_str());
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Actual write of file data
|
|
if (!m_uploadFailed && m_uploadFile)
|
|
{
|
|
if (m_uploadFile.write(data, len) != len)
|
|
m_uploadFailed = true;
|
|
}
|
|
|
|
// close the file and save on final call
|
|
if (final && m_uploadFile)
|
|
{
|
|
m_uploadFile.close();
|
|
if (!m_uploadFailed)
|
|
LOG_INFO("Uploaded file to LittleFS:", filename.c_str());
|
|
}
|
|
}
|
|
|
|
void AstroWebServer::onSetTme(const ArduinoJson::JsonDocument &doc)
|
|
{
|
|
std::string buffer;
|
|
auto epoch = doc["time"].as<time_t>();
|
|
timeval te{
|
|
.tv_sec = epoch,
|
|
.tv_usec = 0,
|
|
};
|
|
timezone tz{
|
|
.tz_minuteswest = 0,
|
|
.tz_dsttime = DST_MET,
|
|
};
|
|
settimeofday(&te, &tz);
|
|
time_t now = time(nullptr);
|
|
struct tm *t = localtime(&now);
|
|
buffer.resize(64);
|
|
strftime(buffer.data(), sizeof(buffer), "%Y-%m-%d %H:%M:%S", t);
|
|
LOG_DEBUG("WS Client set Datetime to: ", buffer.c_str());
|
|
}
|