Simpler Measurement session, fully in memory
This commit is contained in:
@@ -97,41 +97,44 @@ size_t webdavFileListingSpiffs(char *buffer, size_t maxLength,
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
String uriToFileName(const String &uriStr)
|
||||
String uriToFileName(const String &uriStr, const char *spiffsFolder)
|
||||
{
|
||||
String filename;
|
||||
String filename;
|
||||
if (uriStr.endsWith(".st"))
|
||||
filename = uriStr.substring(0, uriStr.length() - strlen(".st"));
|
||||
|
||||
filename = "/dat/" + filename;
|
||||
filename = spiffsFolder + String("/") + filename;
|
||||
return filename;
|
||||
}
|
||||
|
||||
void webdavHandler(httpd_req_t *req)
|
||||
std::function<void(httpd_req_t *)> webdavHandler(const char *uriPrefix,
|
||||
const char *spiffsFolder)
|
||||
{
|
||||
String uri = String(req->uri).substring(strlen("/webdav/"));
|
||||
constexpr size_t WEBDAV_BUFF_LEN = 1024 * 256;
|
||||
static char *webdavBuffer = (char *)heap_caps_malloc(WEBDAV_BUFF_LEN, MALLOC_CAP_SPIRAM);
|
||||
switch (req->method)
|
||||
{
|
||||
case HTTP_GET:
|
||||
case HTTP_PROPFIND:
|
||||
{
|
||||
size_t bytesWritten = webdavFileListingSpiffs(webdavBuffer, WEBDAV_BUFF_LEN, "/dat");
|
||||
httpd_resp_send(req, webdavBuffer, bytesWritten);
|
||||
break;
|
||||
}
|
||||
case HTTP_DELETE:
|
||||
{
|
||||
httpd_resp_set_hdr(req, "Content-Type", "text/plain");
|
||||
return [=](httpd_req_t *req) {
|
||||
String uri = String(req->uri).substring(strlen(uriPrefix));
|
||||
constexpr size_t WEBDAV_BUFF_LEN = 1024 * 256;
|
||||
static char *webdavBuffer = (char *)heap_caps_malloc(WEBDAV_BUFF_LEN, MALLOC_CAP_SPIRAM);
|
||||
switch (req->method)
|
||||
{
|
||||
case HTTP_GET:
|
||||
case HTTP_PROPFIND:
|
||||
{
|
||||
size_t bytesWritten = webdavFileListingSpiffs(webdavBuffer, WEBDAV_BUFF_LEN, spiffsFolder);
|
||||
httpd_resp_send(req, webdavBuffer, bytesWritten);
|
||||
break;
|
||||
}
|
||||
case HTTP_DELETE:
|
||||
{
|
||||
httpd_resp_set_hdr(req, "Content-Type", "text/plain");
|
||||
|
||||
if (portablefs::remove(uriToFileName(uri).c_str()))
|
||||
httpd_resp_send(req, "Deleted file", -1);
|
||||
else
|
||||
if (portablefs::remove(uriToFileName(uri, spiffsFolder).c_str()))
|
||||
httpd_resp_send(req, "Deleted file", -1);
|
||||
else
|
||||
httpd_resp_send_404(req);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
httpd_resp_send_404(req);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
httpd_resp_send_404(req);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,10 @@
|
||||
#include "Dtypes.h"
|
||||
#include "EspHttp.h"
|
||||
|
||||
void webdavHandler(httpd_req_t *req);
|
||||
/**
|
||||
* Handler to serves spiffsFolder via webdav
|
||||
*
|
||||
* this handler has to be entered for multiple HTTP verbs: HTTP_GET, HTTP_PROPFIND, HTTP_DELETE
|
||||
*/
|
||||
std::function<void(httpd_req_t *)> webdavHandler(const char *uriPrefix,
|
||||
const char *spiffsFolder);
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "StreamingMsgPackEncoder.h"
|
||||
#include <cstdint>
|
||||
|
||||
@@ -7,12 +9,12 @@ class SessionChunk
|
||||
{
|
||||
public:
|
||||
SessionChunk()
|
||||
: nextFree(0), sessionStartTime(0), startIndex(0)
|
||||
: nextFree_(0), sessionStartTime(0), startIndex(0)
|
||||
{}
|
||||
|
||||
void init(uint32_t epochStartTime, uint32_t startIdx)
|
||||
{
|
||||
nextFree = 0;
|
||||
nextFree_ = 0;
|
||||
sessionStartTime = epochStartTime;
|
||||
startIndex = startIdx;
|
||||
}
|
||||
@@ -26,15 +28,15 @@ public:
|
||||
}
|
||||
|
||||
uint32_t numMeasurements() const {
|
||||
return nextFree;
|
||||
return nextFree_;
|
||||
}
|
||||
|
||||
bool addPoint(Measurement_T measurement)
|
||||
{
|
||||
if( nextFree >= SIZE)
|
||||
if( nextFree_ >= SIZE)
|
||||
return false;
|
||||
values[nextFree] = measurement;
|
||||
nextFree++;
|
||||
values[nextFree_] = measurement;
|
||||
nextFree_++;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -43,7 +45,7 @@ public:
|
||||
void serialize(StreamingMsgPackEncoder<T> & encoder) const
|
||||
{
|
||||
sendHeader(encoder, sessionStartTime, startIndex);
|
||||
encoder.sendArray(values, nextFree);
|
||||
encoder.sendArray(values, nextFree_);
|
||||
}
|
||||
|
||||
static uint32_t valueOffset()
|
||||
@@ -78,7 +80,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t nextFree = 0;
|
||||
uint32_t nextFree_ = 0;
|
||||
uint32_t sessionStartTime;
|
||||
uint32_t startIndex;
|
||||
Measurement_T values[SIZE];
|
||||
|
||||
129
firmware/lib/session/SimpleMeasurementSession.h
Normal file
129
firmware/lib/session/SimpleMeasurementSession.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "Dtypes.h"
|
||||
#include "SessionChunk.h"
|
||||
#include "FilesystemAbstraction.h"
|
||||
|
||||
template <typename Measurement_T, uint32_t MAX_SIZE>
|
||||
class SimpleMeasurementSession
|
||||
{
|
||||
public:
|
||||
using ChunkT = SessionChunk<Measurement_T, MAX_SIZE>;
|
||||
|
||||
// save interval in number of measurements (by default every minute)
|
||||
SimpleMeasurementSession(uint32_t saveInterval = 10 * 60)
|
||||
: chunk(nullptr), saveInterval_(saveInterval)
|
||||
{
|
||||
}
|
||||
~SimpleMeasurementSession()
|
||||
{
|
||||
if (chunk != nullptr)
|
||||
free(chunk);
|
||||
}
|
||||
|
||||
void init(uint32_t epochStartTime)
|
||||
{
|
||||
if (chunk == nullptr)
|
||||
{
|
||||
// psram allocation doesn't seem to work in constructor
|
||||
chunk = (ChunkT *)heap_caps_malloc(sizeof(ChunkT), MALLOC_CAP_SPIRAM);
|
||||
new (chunk) ChunkT(); // placement new to init chunk
|
||||
}
|
||||
chunk->init(epochStartTime, 0);
|
||||
}
|
||||
|
||||
bool addPoint(Measurement_T measurement)
|
||||
{
|
||||
bool success = chunk->addPoint(measurement);
|
||||
if (success && (chunk->numMeasurements() % saveInterval_) == 0)
|
||||
saveToFileSystem();
|
||||
if(!success)
|
||||
Serial.println("Failed to add point");
|
||||
//Serial.printf("Add point %d success %d\n", measurement, success);
|
||||
return success;
|
||||
}
|
||||
|
||||
void finalize()
|
||||
{
|
||||
saveToFileSystem();
|
||||
chunk->init(0, 0);
|
||||
}
|
||||
|
||||
uint32_t getStartTime() const
|
||||
{
|
||||
return chunk->getStartTime();
|
||||
}
|
||||
|
||||
uint32_t numMeasurements() const
|
||||
{
|
||||
return chunk->numMeasurements();
|
||||
}
|
||||
|
||||
template <typename Encoder_T>
|
||||
void serialize(Encoder_T &encoder, uint32_t startIdx) const
|
||||
{
|
||||
ChunkT::sendHeader(encoder, chunk->getStartTime(), startIdx);
|
||||
auto numElementsToSend = chunk->numMeasurements() - startIdx;
|
||||
encoder.sendArray(chunk->getDataPointer() + startIdx, numElementsToSend);
|
||||
}
|
||||
|
||||
private:
|
||||
void saveToFileSystem()
|
||||
{
|
||||
// todo: check this! free doesn't mean that the file writing actually works ok
|
||||
// use error codes of write instead? anyway: test it!
|
||||
deleteUntilBytesFree(CONFIG_SESSION_MAX_SIZE);
|
||||
|
||||
String filename = String(CONFIG_DATA_PATH) + "/" + String(chunk->getStartTime());
|
||||
if (portablefs::exists(filename.c_str()))
|
||||
{
|
||||
auto file = portablefs::open(filename.c_str(), "a");
|
||||
file.seek(0, SeekEnd);
|
||||
size_t existingMeasurements = (file.size() - ChunkT::valueOffset()) / sizeof(Measurement_T);
|
||||
Serial.printf("Incremental save, existing %d\n", existingMeasurements);
|
||||
size_t measurementsToWrite = chunk->numMeasurements() - existingMeasurements;
|
||||
Measurement_T *startPtr = chunk->getDataPointer() + existingMeasurements;
|
||||
file.write((uint8_t *)(startPtr), measurementsToWrite * sizeof(Measurement_T));
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.println("First save");
|
||||
auto file = portablefs::open(filename.c_str(), "w");
|
||||
StreamingMsgPackEncoder<portablefs::File> encoder(&file);
|
||||
chunk->serialize(encoder);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteUntilBytesFree(size_t requiredSpace)
|
||||
{
|
||||
auto freeBytes = portablefs::totalBytes() - portablefs::usedBytes();
|
||||
while (freeBytes < requiredSpace)
|
||||
{
|
||||
uint32_t nextSessionToDelete = 0;
|
||||
|
||||
auto dir = portablefs::openDir(CONFIG_DATA_PATH);
|
||||
String filenameToDelete;
|
||||
while (dir.next())
|
||||
{
|
||||
if (dir.isFile())
|
||||
{
|
||||
const auto fileName = dir.fileName();
|
||||
const auto fileNameWithoutDir = fileName.substring(fileName.lastIndexOf("/") + 1);
|
||||
auto sessionId = fileNameWithoutDir.toInt();
|
||||
if (sessionId < nextSessionToDelete)
|
||||
{
|
||||
nextSessionToDelete = sessionId;
|
||||
filenameToDelete = dir.fileName();
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(nextSessionToDelete > 0);
|
||||
Serial.printf("Removing old session %s to make space\n", filenameToDelete.c_str());
|
||||
portablefs::remove(filenameToDelete.c_str());
|
||||
auto newFreeBytes = portablefs::totalBytes() - portablefs::usedBytes();
|
||||
assert(newFreeBytes > freeBytes);
|
||||
freeBytes = newFreeBytes;
|
||||
}
|
||||
}
|
||||
|
||||
ChunkT *chunk;
|
||||
uint32_t saveInterval_;
|
||||
};
|
||||
Reference in New Issue
Block a user