swimtracker-firmware/firmware/lib/session/SimpleMeasurementSession.h

146 lines
4.9 KiB
C
Raw Normal View History

#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>;
2020-06-25 22:01:53 +02:00
using MeasurementType = Measurement_T;
// save interval in number of measurements (by default every minute)
2020-06-28 10:30:01 +02:00
SimpleMeasurementSession(uint32_t saveInterval = 10 * 20)
: chunk(nullptr), saveInterval_(saveInterval)
{
}
2020-06-21 16:03:00 +02:00
~SimpleMeasurementSession()
{
if (chunk != nullptr)
free(chunk);
}
void init(uint32_t epochStartTime)
{
2020-06-21 16:03:00 +02:00
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();
2020-06-21 16:03:00 +02:00
if (!success)
Serial.println("Failed to add point");
return success;
}
void finalize()
{
2020-06-21 16:03:00 +02:00
if (numMeasurements() > 0)
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);
}
2020-06-25 22:01:53 +02:00
Measurement_T *getDataPointer()
{
return chunk->getDataPointer();
}
private:
void saveToFileSystem()
{
2020-06-28 11:16:34 +02:00
static const uint32_t arrayHeaderOffset = ChunkT::arrayHeaderOffset();
const uint32_t numMeasurements = chunk->numMeasurements();
// todo: check this! free doesn't mean that the file writing actually works ok
// use error codes of write instead? anyway: test it!
Serial.printf("%ld saveToFileSystem start\n", millis());
deleteUntilBytesFree(CONFIG_SESSION_MAX_SIZE);
Serial.printf(" %ld after deleteUntilBytesFree()\n", millis());
String filename = String(CONFIG_DATA_PATH) + "/" + String(chunk->getStartTime());
if (portablefs::exists(filename.c_str()))
{
2020-06-28 11:16:34 +02:00
auto file = portablefs::open(filename.c_str(), "r+");
file.seek(0, SeekEnd);
size_t existingMeasurements = (file.size() - ChunkT::valueOffset()) / sizeof(Measurement_T);
2020-06-28 11:16:34 +02:00
size_t measurementsToWrite = numMeasurements - existingMeasurements;
Measurement_T *startPtr = chunk->getDataPointer() + existingMeasurements;
file.write((uint8_t *)(startPtr), measurementsToWrite * sizeof(Measurement_T));
2020-06-28 10:30:01 +02:00
2020-06-28 11:16:34 +02:00
file.seek(arrayHeaderOffset);
2020-06-28 10:30:01 +02:00
StreamingMsgPackEncoder<portablefs::File> encoder(&file);
2020-06-28 11:16:34 +02:00
encoder.template sendArrayHeader<Measurement_T>(numMeasurements);
}
else
{
auto file = portablefs::open(filename.c_str(), "w");
StreamingMsgPackEncoder<portablefs::File> encoder(&file);
chunk->serialize(encoder);
}
Serial.printf(" %ld saveToFileSystem done\n", millis());
}
void deleteUntilBytesFree(size_t requiredSpace)
{
auto freeBytes = portablefs::totalBytes() - portablefs::usedBytes();
while (freeBytes < requiredSpace)
{
2021-07-23 10:34:34 +02:00
uint32_t nextSessionToDelete = uint32_t(-1);
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);
2021-07-23 10:34:34 +02:00
uint32_t sessionId = fileNameWithoutDir.toInt();
if (sessionId < nextSessionToDelete)
{
nextSessionToDelete = sessionId;
filenameToDelete = dir.fileName();
}
}
}
assert(nextSessionToDelete > 0);
2021-07-23 10:34:34 +02:00
assert(nextSessionToDelete < uint32_t(-1));
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_;
};