eive-obsw/mission/tmtc/PersistentTmStore.cpp

428 lines
15 KiB
C++
Raw Normal View History

2023-02-07 12:23:00 +01:00
#include "PersistentTmStore.h"
2022-11-11 15:39:27 +01:00
2022-10-25 18:20:21 +02:00
#include <mission/memory/SdCardMountedIF.h>
2023-03-09 13:15:42 +01:00
#include <mission/tmtc/DirectTmSinkIF.h>
2022-10-25 18:20:21 +02:00
2022-11-11 15:39:27 +01:00
#include <algorithm>
2022-12-12 14:58:56 +01:00
#include <cinttypes>
2022-12-12 16:18:00 +01:00
#include <filesystem>
2022-12-12 18:42:51 +01:00
#include <fstream>
2022-12-13 15:56:40 +01:00
#include <utility>
2022-10-24 10:57:30 +02:00
2023-04-01 14:23:11 +02:00
#include "eive/definitions.h"
2023-02-21 20:43:16 +01:00
#include "fsfw/ipc/CommandMessage.h"
#include "fsfw/ipc/QueueFactory.h"
#include "fsfw/tmstorage/TmStoreMessage.h"
2023-03-10 19:01:31 +01:00
#include "mission/persistentTmStoreDefs.h"
2023-02-21 20:43:16 +01:00
2022-10-24 10:57:30 +02:00
using namespace returnvalue;
2023-03-09 19:42:20 +01:00
PersistentTmStore::PersistentTmStore(PersistentTmStoreArgs args)
: SystemObject(args.objectId),
tmStore(args.tmStore),
baseDir(args.baseDir),
baseName(std::move(args.baseName)),
sdcMan(args.sdcMan) {
2023-02-21 20:43:16 +01:00
tcQueue = QueueFactory::instance()->createMessageQueue();
2023-03-09 19:42:20 +01:00
calcDiffSeconds(args.intervalUnit, args.intervalCount);
2022-12-12 18:27:01 +01:00
}
2022-10-24 10:57:30 +02:00
2023-03-10 20:32:11 +01:00
ReturnValue_t PersistentTmStore::cancelDump() {
state = State::IDLE;
return returnvalue::OK;
}
2023-06-23 14:55:51 +02:00
ReturnValue_t PersistentTmStore::buildDumpSet(uint32_t fromUnixSeconds, uint32_t upToUnixSeconds) {
using namespace std::filesystem;
std::error_code e;
dumpParams.orderedDumpFilestamps.clear();
for (auto const& fileOrDir : directory_iterator(basePath)) {
if (not fileOrDir.is_regular_file(e)) {
continue;
}
2023-06-23 16:05:38 +02:00
dumpParams.fileSize = std::filesystem::file_size(fileOrDir.path(), e);
2023-06-23 14:55:51 +02:00
if (e) {
sif::error << "PersistentTmStore: Could not retrieve file size: " << e.message() << std::endl;
continue;
}
// File empty or can't even read CCSDS header.
if (dumpParams.fileSize <= 6) {
continue;
}
if (dumpParams.fileSize > fileBuf.size()) {
sif::error << "PersistentTmStore: File too large, is deleted" << std::endl;
triggerEvent(persTmStore::FILE_TOO_LARGE, dumpParams.fileSize, fileBuf.size());
2023-06-23 16:05:38 +02:00
std::filesystem::remove(fileOrDir.path(), e);
2023-06-23 14:55:51 +02:00
continue;
}
const path& file = fileOrDir.path();
struct tm fileTime {};
if (pathToTime(file, fileTime) != returnvalue::OK) {
sif::error << "Time extraction for file " << file << "failed" << std::endl;
continue;
}
auto fileEpoch = static_cast<uint32_t>(timegm(&fileTime));
if ((fileEpoch > dumpParams.fromUnixTime) and
(fileEpoch + rolloverDiffSeconds <= dumpParams.untilUnixTime)) {
std::ifstream ifile(file, std::ios::binary);
if (ifile.bad()) {
sif::error << "PersistentTmStore: File is bad" << std::endl;
// TODO: Consider deleting file here?
continue;
}
DumpIndex dumpIndex;
dumpIndex.epoch = fileEpoch;
2023-06-23 16:05:38 +02:00
dumpIndex.suffixIdx = extractSuffix(file.string());
2023-06-24 02:07:11 +02:00
// sif::debug << "Inserting file " << fileOrDir.path() << std::endl;
2023-06-23 14:55:51 +02:00
dumpParams.orderedDumpFilestamps.emplace(dumpIndex);
}
}
return returnvalue::OK;
}
2023-06-23 17:07:07 +02:00
std::optional<uint8_t> PersistentTmStore::extractSuffix(const std::string& pathStr) {
2023-06-23 16:05:38 +02:00
std::string numberStr;
// Find the position of the dot at the end of the file path
size_t dotPos = pathStr.find_last_of('.');
2023-06-24 02:07:11 +02:00
if ((dotPos < pathStr.length()) and not std::isdigit(pathStr[dotPos + 1])) {
return std::nullopt;
2023-06-23 16:05:38 +02:00
}
2023-06-24 02:07:11 +02:00
// Extract the substring after the dot
numberStr = pathStr.substr(dotPos + 1);
2023-06-23 17:07:07 +02:00
std::optional<uint8_t> number;
try {
number = std::stoi(numberStr);
if (number < 0 or number > std::numeric_limits<uint8_t>::max()) {
return number;
}
} catch (std::invalid_argument& exception) {
sif::error << "PersistentTmStore::extractSuffix: Exception " << exception.what()
<< ", invald input string: " << numberStr << std::endl;
2023-06-23 16:05:38 +02:00
}
2023-06-23 17:07:07 +02:00
return number;
2023-06-23 16:05:38 +02:00
}
2023-02-22 13:27:16 +01:00
ReturnValue_t PersistentTmStore::assignAndOrCreateMostRecentFile() {
if (not activeFile.has_value()) {
2023-02-24 18:10:43 +01:00
return createMostRecentFile(std::nullopt);
2023-02-22 13:27:16 +01:00
}
return returnvalue::OK;
}
2023-03-10 20:32:11 +01:00
ReturnValue_t PersistentTmStore::handleCommandQueue(StorageManagerIF& ipcStore,
Command_t& execCmd) {
2023-02-21 20:43:16 +01:00
CommandMessage cmdMessage;
ReturnValue_t result = tcQueue->receiveMessage(&cmdMessage);
if (result != returnvalue::OK) {
return result;
}
if (cmdMessage.getMessageType() == messagetypes::TM_STORE) {
Command_t cmd = cmdMessage.getCommand();
if (cmd == TmStoreMessage::DELETE_STORE_CONTENT_TIME) {
2023-02-24 18:10:43 +01:00
Clock::getClock_timeval(&currentTv);
2023-02-21 20:43:16 +01:00
store_address_t storeId = TmStoreMessage::getStoreId(&cmdMessage);
auto accessor = ipcStore.getData(storeId);
uint32_t deleteUpToUnixSeconds = 0;
size_t size = accessor.second.size();
SerializeAdapter::deSerialize(&deleteUpToUnixSeconds, accessor.second.data(), &size,
SerializeIF::Endianness::NETWORK);
2023-03-10 20:32:11 +01:00
execCmd = cmd;
2023-02-21 20:43:16 +01:00
deleteUpTo(deleteUpToUnixSeconds);
} else if (cmd == TmStoreMessage::DOWNLINK_STORE_CONTENT_TIME) {
2023-02-24 18:10:43 +01:00
Clock::getClock_timeval(&currentTv);
2023-02-21 20:43:16 +01:00
store_address_t storeId = TmStoreMessage::getStoreId(&cmdMessage);
auto accessor = ipcStore.getData(storeId);
2023-02-22 13:27:16 +01:00
if (accessor.second.size() < 8) {
return returnvalue::FAILED;
}
uint32_t dumpFromUnixSeconds = 0;
uint32_t dumpUntilUnixSeconds = 0;
2023-02-22 13:27:16 +01:00
size_t size = 8;
2023-02-21 20:43:16 +01:00
SerializeAdapter::deSerialize(&dumpFromUnixSeconds, accessor.second.data(), &size,
SerializeIF::Endianness::NETWORK);
2023-02-22 13:27:16 +01:00
SerializeAdapter::deSerialize(&dumpUntilUnixSeconds, accessor.second.data() + 4, &size,
2023-02-21 20:43:16 +01:00
SerializeIF::Endianness::NETWORK);
2023-03-09 17:44:05 +01:00
result = startDumpFromUpTo(dumpFromUnixSeconds, dumpUntilUnixSeconds);
2023-03-10 19:34:32 +01:00
if (result == BUSY_DUMPING) {
2023-03-10 19:01:31 +01:00
triggerEvent(persTmStore::BUSY_DUMPING_EVENT);
2023-03-10 20:32:11 +01:00
} else {
execCmd = cmd;
2023-03-09 17:44:05 +01:00
}
2023-02-21 20:43:16 +01:00
}
}
2023-03-10 20:32:11 +01:00
return result;
2023-02-21 20:43:16 +01:00
}
2023-03-09 12:20:13 +01:00
ReturnValue_t PersistentTmStore::startDumpFrom(uint32_t fromUnixSeconds) {
return startDumpFromUpTo(fromUnixSeconds, currentTv.tv_sec);
2023-02-07 15:22:01 +01:00
}
2023-02-21 21:37:30 +01:00
ReturnValue_t PersistentTmStore::storePacket(PusTmReader& reader) {
2022-12-12 10:06:30 +01:00
using namespace std::filesystem;
2022-12-12 18:27:01 +01:00
if (baseDirUninitialized) {
updateBaseDir();
2022-12-12 10:06:30 +01:00
}
2023-02-24 18:10:43 +01:00
Clock::getClock_timeval(&currentTv);
2022-12-12 18:27:01 +01:00
// It is assumed here that the filesystem is usable.
2023-02-22 13:27:16 +01:00
if (not activeFile.has_value()) {
ReturnValue_t result = assignAndOrCreateMostRecentFile();
if (result != returnvalue::OK) {
return result;
}
2022-12-12 10:06:30 +01:00
}
2022-12-12 18:42:51 +01:00
2023-02-24 18:10:43 +01:00
bool createNewFile = false;
std::optional<uint8_t> suffix = std::nullopt;
2023-03-10 17:32:22 +01:00
std::error_code e;
size_t fileSize = file_size(activeFile.value(), e);
if (e) {
sif::error << "PersistentTmStore: Could not retrieve file size, "
"error "
<< e.message() << std::endl;
return returnvalue::FAILED;
}
2023-02-24 18:10:43 +01:00
if (currentTv.tv_sec > activeFileTv.tv_sec + static_cast<int>(rolloverDiffSeconds)) {
createNewFile = true;
currentSameSecNumber = 0;
2023-03-10 17:32:22 +01:00
} else if (fileSize + reader.getFullPacketLen() > fileBuf.size()) {
2023-02-24 18:10:43 +01:00
createNewFile = true;
if (currentSameSecNumber >= MAX_FILES_IN_ONE_SECOND) {
currentSameSecNumber = 0;
2022-12-12 18:42:51 +01:00
}
2023-02-24 18:10:43 +01:00
if (currentTv.tv_sec == activeFileTv.tv_sec) {
suffix = currentSameSecNumber++;
} else {
currentSameSecNumber = 0;
}
}
if (createNewFile) {
2023-02-24 18:20:32 +01:00
createMostRecentFile(suffix);
2022-12-12 18:42:51 +01:00
}
// Rollover conditions were handled, write to file now
2023-02-22 13:27:16 +01:00
std::ofstream of(activeFile.value(), std::ios::app | std::ios::binary);
2023-02-21 20:43:16 +01:00
of.write(reinterpret_cast<const char*>(reader.getFullData()),
static_cast<std::streamsize>(reader.getFullPacketLen()));
2022-12-12 10:06:30 +01:00
return returnvalue::OK;
}
2022-11-11 15:39:27 +01:00
2023-02-21 21:37:30 +01:00
MessageQueueId_t PersistentTmStore::getCommandQueue() const { return tcQueue->getId(); }
2022-12-12 18:27:01 +01:00
2023-02-21 21:37:30 +01:00
void PersistentTmStore::calcDiffSeconds(RolloverInterval intervalUnit, uint32_t intervalCount) {
2022-12-13 15:34:13 +01:00
if (intervalUnit == RolloverInterval::MINUTELY) {
rolloverDiffSeconds = 60 * intervalCount;
} else if (intervalUnit == RolloverInterval::HOURLY) {
2022-12-13 13:46:49 +01:00
rolloverDiffSeconds = 60 * 60 * intervalCount;
2022-12-12 18:27:01 +01:00
} else if (intervalUnit == RolloverInterval::DAILY) {
2022-12-13 13:46:49 +01:00
rolloverDiffSeconds = 60 * 60 * 24 * intervalCount;
2022-12-12 18:27:01 +01:00
}
}
2023-02-22 18:06:34 +01:00
bool PersistentTmStore::updateBaseDir() {
2022-12-12 18:27:01 +01:00
using namespace std::filesystem;
2023-02-22 18:06:34 +01:00
const char* currentPrefix = sdcMan.getCurrentMountPrefix();
if (currentPrefix == nullptr) {
return false;
}
2022-12-14 10:34:23 +01:00
basePath = path(currentPrefix) / baseDir / baseName;
2023-03-08 14:50:25 +01:00
std::error_code e;
if (not exists(basePath, e)) {
2022-12-14 10:34:23 +01:00
create_directories(basePath);
2022-12-12 18:27:01 +01:00
}
2023-06-23 14:55:51 +02:00
// Each file will have the base name as a prefix again
path preparedFullFilePath = basePath / baseName;
basePathSize = preparedFullFilePath.string().length();
std::memcpy(filePathBuf.data(), preparedFullFilePath.c_str(), basePathSize);
2023-06-23 17:07:07 +02:00
filePathBuf[basePathSize] = '_';
basePathSize += 1;
2022-12-12 18:27:01 +01:00
baseDirUninitialized = false;
2023-02-22 18:06:34 +01:00
return true;
2022-12-12 18:27:01 +01:00
}
2023-02-21 21:37:30 +01:00
void PersistentTmStore::deleteUpTo(uint32_t unixSeconds) {
2023-02-07 12:19:13 +01:00
using namespace std::filesystem;
for (auto const& file : directory_iterator(basePath)) {
2023-02-22 13:27:16 +01:00
if (file.is_directory() or (activeFile.has_value() and (activeFile.value() == file.path()))) {
2023-02-07 12:19:13 +01:00
continue;
}
2023-02-22 13:27:16 +01:00
// Convert file time to the UNIX epoch
struct tm fileTime {};
2023-03-09 12:20:13 +01:00
if (pathToTime(file.path(), fileTime) != returnvalue::OK) {
2023-03-27 23:09:26 +02:00
sif::error << "Time extraction for " << file << " failed" << std::endl;
2023-02-07 12:19:13 +01:00
continue;
}
2023-02-22 14:21:24 +01:00
time_t fileEpoch = timegm(&fileTime);
if (fileEpoch + rolloverDiffSeconds < unixSeconds) {
2023-03-10 17:32:22 +01:00
std::error_code e;
std::filesystem::remove(file.path(), e);
2023-02-07 12:19:13 +01:00
}
}
}
2022-12-19 13:25:45 +01:00
2023-03-09 12:20:13 +01:00
ReturnValue_t PersistentTmStore::startDumpFromUpTo(uint32_t fromUnixSeconds,
uint32_t upToUnixSeconds) {
2023-02-07 12:19:13 +01:00
using namespace std::filesystem;
2023-03-09 12:20:13 +01:00
if (state == State::DUMPING) {
2023-02-22 13:27:16 +01:00
return returnvalue::FAILED;
}
2023-06-23 14:55:51 +02:00
auto dirIter = directory_iterator(basePath);
// Directory empty case.
if (dirIter == directory_iterator()) {
2023-03-10 18:27:10 +01:00
return returnvalue::FAILED;
}
2023-03-09 13:15:42 +01:00
dumpParams.fromUnixTime = fromUnixSeconds;
dumpParams.untilUnixTime = upToUnixSeconds;
2023-06-23 14:55:51 +02:00
buildDumpSet(fromUnixSeconds, upToUnixSeconds);
// No files in time window found.
if (dumpParams.orderedDumpFilestamps.empty()) {
return DUMP_DONE;
}
dumpParams.dumpIter = dumpParams.orderedDumpFilestamps.begin();
2023-03-09 12:20:13 +01:00
state = State::DUMPING;
2023-03-23 19:22:14 +01:00
return loadNextDumpFile();
2023-02-07 12:19:13 +01:00
}
2023-03-09 13:15:42 +01:00
ReturnValue_t PersistentTmStore::loadNextDumpFile() {
using namespace std::filesystem;
2023-03-10 18:04:04 +01:00
dumpParams.currentSize = 0;
2023-03-09 13:15:42 +01:00
std::error_code e;
2023-06-23 14:55:51 +02:00
for (; dumpParams.dumpIter != dumpParams.orderedDumpFilestamps.end(); dumpParams.dumpIter++) {
DumpIndex dumpIndex = *dumpParams.dumpIter;
timeval tv{};
tv.tv_sec = dumpIndex.epoch;
size_t fullPathLength = 0;
createFileName(tv, dumpIndex.suffixIdx, fullPathLength);
2023-06-23 16:05:38 +02:00
dumpParams.currentFile =
path(std::string(reinterpret_cast<const char*>(filePathBuf.data()), fullPathLength));
2023-06-24 02:07:11 +02:00
dumpParams.fileSize = std::filesystem::file_size(dumpParams.currentFile, e);
if (e) {
// TODO: Event?
sif::error << "PersistentTmStore: Could not load next dump file: " << e.message()
<< std::endl;
continue;
}
2023-06-23 16:05:38 +02:00
std::ifstream ifile(dumpParams.currentFile, std::ios::binary);
2023-06-23 14:55:51 +02:00
if (ifile.bad()) {
2023-06-24 02:07:11 +02:00
sif::error << "PersistentTmStore: File is bad. Loading next file" << std::endl;
2023-03-09 13:15:42 +01:00
continue;
}
2023-06-24 02:07:11 +02:00
// sif::debug << baseName << " dump: Loading " << dumpParams.currentFile << std::endl;
2023-06-23 14:55:51 +02:00
ifile.read(reinterpret_cast<char*>(fileBuf.data()),
static_cast<std::streamsize>(dumpParams.fileSize));
// Increment iterator for next cycle.
dumpParams.dumpIter++;
return returnvalue::OK;
2023-02-07 12:19:13 +01:00
}
2023-03-23 20:56:06 +01:00
// Directory iterator was consumed and we are done.
state = State::IDLE;
return DUMP_DONE;
2022-12-19 14:40:27 +01:00
}
2023-02-22 13:27:16 +01:00
2023-03-31 13:17:15 +02:00
ReturnValue_t PersistentTmStore::getNextDumpPacket(PusTmReader& reader, bool& fileHasSwapped) {
if (state == State::IDLE or dumpParams.pendingPacketDump) {
2023-03-09 13:15:42 +01:00
return returnvalue::FAILED;
}
2023-05-25 08:48:03 +02:00
fileHasSwapped = false;
2023-03-31 13:17:15 +02:00
reader.setReadOnlyData(fileBuf.data() + dumpParams.currentSize,
fileBuf.size() - dumpParams.currentSize);
2023-03-09 13:15:42 +01:00
// CRC check to fully ensure this is a valid TM
ReturnValue_t result = reader.parseDataWithCrcCheck();
2023-03-31 13:17:15 +02:00
if (result != returnvalue::OK) {
2023-03-10 18:04:04 +01:00
sif::error << "PersistentTmStore: Parsing of PUS TM failed with code " << result << std::endl;
2023-03-10 19:01:31 +01:00
triggerEvent(persTmStore::POSSIBLE_FILE_CORRUPTION, result, dumpParams.currentFileUnixStamp);
2023-03-09 13:15:42 +01:00
// Delete the file and load next. Could use better algorithm to partially
// restore the file dump, but for now do not trust the file.
2023-03-10 17:32:22 +01:00
std::error_code e;
2023-06-23 16:05:38 +02:00
std::filesystem::remove(dumpParams.currentFile.c_str(), e);
if (dumpParams.currentFile == activeFile) {
activeFile == std::nullopt;
assignAndOrCreateMostRecentFile();
}
2023-03-09 20:44:55 +01:00
fileHasSwapped = true;
2023-03-09 13:15:42 +01:00
return loadNextDumpFile();
}
2023-03-31 13:17:15 +02:00
dumpParams.pendingPacketDump = true;
return returnvalue::OK;
}
ReturnValue_t PersistentTmStore::confirmDump(const PusTmReader& reader, bool& fileHasSwapped) {
dumpParams.pendingPacketDump = false;
dumpParams.currentSize += reader.getFullPacketLen();
if (dumpParams.currentSize >= dumpParams.fileSize) {
fileHasSwapped = true;
return loadNextDumpFile();
}
fileHasSwapped = false;
2023-03-09 13:15:42 +01:00
return returnvalue::OK;
}
2023-03-09 12:20:13 +01:00
ReturnValue_t PersistentTmStore::pathToTime(const std::filesystem::path& path, struct tm& time) {
auto pathStr = path.string();
size_t splitChar = pathStr.find('_');
auto timeOnlyStr = pathStr.substr(splitChar + 1);
2023-04-01 14:23:11 +02:00
if (nullptr == strptime(timeOnlyStr.c_str(), config::FILE_DATE_FORMAT, &time)) {
2023-03-09 12:20:13 +01:00
return returnvalue::FAILED;
}
return returnvalue::OK;
}
2023-02-24 18:10:43 +01:00
ReturnValue_t PersistentTmStore::createMostRecentFile(std::optional<uint8_t> suffix) {
2023-02-22 13:27:16 +01:00
using namespace std::filesystem;
2023-06-23 14:55:51 +02:00
size_t currentIdx;
createFileName(currentTv, suffix, currentIdx);
path newPath(std::string(reinterpret_cast<const char*>(filePathBuf.data()), currentIdx));
std::ofstream of(newPath, std::ios::binary);
activeFile = newPath;
activeFileTv = currentTv;
return returnvalue::OK;
}
ReturnValue_t PersistentTmStore::initializeTmStore() {
Clock::getClock_timeval(&currentTv);
updateBaseDir();
// Reset active file, base directory might have changed.
activeFile = std::nullopt;
return assignAndOrCreateMostRecentFile();
}
PersistentTmStore::State PersistentTmStore::getState() const { return state; }
void PersistentTmStore::getStartAndEndTimeCurrentOrLastDump(uint32_t& startTime,
uint32_t& endTime) const {
startTime = dumpParams.fromUnixTime;
endTime = dumpParams.untilUnixTime;
}
ReturnValue_t PersistentTmStore::createFileName(timeval& tv, std::optional<uint8_t> suffix,
size_t& fullPathLength) {
unsigned currentIdx = basePathSize;
time_t epoch = tv.tv_sec;
2023-02-22 13:27:16 +01:00
struct tm* time = gmtime(&epoch);
2023-06-23 14:55:51 +02:00
size_t writtenBytes = strftime(reinterpret_cast<char*>(filePathBuf.data() + currentIdx),
filePathBuf.size(), config::FILE_DATE_FORMAT, time);
2023-02-22 13:27:16 +01:00
if (writtenBytes == 0) {
sif::error << "PersistentTmStore::createMostRecentFile: Could not create file timestamp"
<< std::endl;
return returnvalue::FAILED;
}
currentIdx += writtenBytes;
2023-06-23 14:55:51 +02:00
char* res = strcpy(reinterpret_cast<char*>(filePathBuf.data() + currentIdx), ".bin");
2023-02-24 18:10:43 +01:00
if (res == nullptr) {
return returnvalue::FAILED;
}
2023-02-22 13:27:16 +01:00
currentIdx += 4;
2023-02-24 18:10:43 +01:00
if (suffix.has_value()) {
std::string fullSuffix = "." + std::to_string(suffix.value());
2023-06-23 14:55:51 +02:00
res = strcpy(reinterpret_cast<char*>(filePathBuf.data() + currentIdx), fullSuffix.c_str());
2023-02-24 18:10:43 +01:00
if (res == nullptr) {
return returnvalue::FAILED;
}
currentIdx += fullSuffix.size();
}
2023-06-23 14:55:51 +02:00
fullPathLength = currentIdx;
2023-02-22 13:27:16 +01:00
return returnvalue::OK;
}