diff --git a/mission/tmtc/PersistentTmStore.cpp b/mission/tmtc/PersistentTmStore.cpp index c6f59007..91a8be48 100644 --- a/mission/tmtc/PersistentTmStore.cpp +++ b/mission/tmtc/PersistentTmStore.cpp @@ -28,6 +28,42 @@ PersistentTmStore::PersistentTmStore(object_id_t objectId, const char* baseDir, calcDiffSeconds(intervalUnit, intervalCount); } +ReturnValue_t PersistentTmStore::assignAndOrCreateMostRecentFile() { + using namespace std::filesystem; + for (auto const& file : directory_iterator(basePath)) { + if (file.is_directory()) { + continue; + } + auto pathStr = file.path().string(); + if (pathStr.find(baseName) == std::string::npos) { + continue; + } + unsigned int underscorePos = pathStr.find_last_of('_'); + std::string stampStr = pathStr.substr(underscorePos + 1); + struct tm time {}; + if (nullptr == strptime(stampStr.c_str(), FILE_DATE_FORMAT, &time)) { + sif::error << "PersistentTmStore::assignOrCreateMostRecentFile: Error reading timestamp" + << std::endl; + // Delete the file and re-create it. + activeFile = std::nullopt; + std::filesystem::remove(file.path()); + break; + } + time_t fileEpoch = timegm(&time); + // There is still a file within the active time window, so re-use that file for new TMs to + // store. + if (fileEpoch + rolloverDiffSeconds > currentTv.tv_sec) { + activeFileTv.tv_sec = fileEpoch; + activeFile = file.path(); + break; + } + } + if (not activeFile.has_value()) { + return createMostRecentFile(); + } + return returnvalue::OK; +} + ReturnValue_t PersistentTmStore::handleCommandQueue(StorageManagerIF& ipcStore, TmFunnelBase& tmFunnel) { CommandMessage cmdMessage; @@ -51,14 +87,16 @@ ReturnValue_t PersistentTmStore::handleCommandQueue(StorageManagerIF& ipcStore, } else if (cmd == TmStoreMessage::DOWNLINK_STORE_CONTENT_TIME) { store_address_t storeId = TmStoreMessage::getStoreId(&cmdMessage); auto accessor = ipcStore.getData(storeId); + if (accessor.second.size() < 8) { + return returnvalue::FAILED; + } uint32_t dumpFromUnixSeconds; uint32_t dumpUntilUnixSeconds; - size_t size = accessor.second.size(); + size_t size = 8; SerializeAdapter::deSerialize(&dumpFromUnixSeconds, accessor.second.data(), &size, SerializeIF::Endianness::NETWORK); - SerializeAdapter::deSerialize(&dumpUntilUnixSeconds, accessor.second.data(), &size, + SerializeAdapter::deSerialize(&dumpUntilUnixSeconds, accessor.second.data() + 4, &size, SerializeIF::Endianness::NETWORK); - // TODO: TM store missing, and maybe there is a better way to do this? dumpFromUpTo(dumpFromUnixSeconds, dumpUntilUnixSeconds, tmFunnel); } } @@ -110,30 +148,32 @@ ReturnValue_t PersistentTmStore::storePacket(PusTmReader& reader) { updateBaseDir(); } // It is assumed here that the filesystem is usable. - if (not mostRecentFile) { - assignAndOrCreateMostRecentFile(); + if (not activeFile.has_value()) { + ReturnValue_t result = assignAndOrCreateMostRecentFile(); + if (result != returnvalue::OK) { + return result; + } } - if (currentTv.tv_sec < mostRecentTv.value().tv_sec or - currentTv.tv_sec - mostRecentTv.value().tv_sec > static_cast(rolloverDiffSeconds)) { - if (file_size(mostRecentFile.value()) + reader.getFullPacketLen() > fileBuf.size()) { + if (currentTv.tv_sec < activeFileTv.tv_sec or + currentTv.tv_sec - activeFileTv.tv_sec > static_cast(rolloverDiffSeconds)) { + if (file_size(activeFile.value()) + reader.getFullPacketLen() > fileBuf.size()) { uint8_t appendedCounter = 1; path rolloverName; while (true) { - rolloverName = - path(mostRecentFile.value().string() + "." + std::to_string(appendedCounter)); + rolloverName = path(activeFile.value().string() + "." + std::to_string(appendedCounter)); if (not exists(rolloverName)) { break; } appendedCounter++; } - rename(mostRecentFile.value(), rolloverName); - std::ofstream of(mostRecentFile.value(), std::ios::binary); + rename(activeFile.value(), rolloverName); + std::ofstream of(activeFile.value(), std::ios::binary); } } // Rollover conditions were handled, write to file now - std::ofstream of(mostRecentFile.value(), std::ios::app | std::ios::binary); + std::ofstream of(activeFile.value(), std::ios::app | std::ios::binary); of.write(reinterpret_cast(reader.getFullData()), static_cast(reader.getFullPacketLen())); return returnvalue::OK; @@ -161,51 +201,6 @@ void PersistentTmStore::updateBaseDir() { baseDirUninitialized = false; } -void PersistentTmStore::assignAndOrCreateMostRecentFile() { - using namespace std::filesystem; - for (auto const& file : directory_iterator(basePath)) { - if (file.is_directory()) { - continue; - } - auto pathStr = file.path().string(); - Clock::TimeOfDay_t tod; - if (pathStr.find(baseName) == std::string::npos) { - continue; - } - unsigned int underscorePos = pathStr.find_last_of('_'); - std::string stampStr = pathStr.substr(underscorePos + 1); - int count = sscanf(stampStr.c_str(), - "%04" SCNu32 "-%02" SCNu32 "-%02" SCNu32 "T%02" SCNu32 "-%02" SCNu32 - "-%02" SCNu32 "Z", - &tod.year, &tod.month, &tod.day, &tod.hour, &tod.minute, &tod.second); - if (count != 6) { - continue; - } - timeval tv{}; - Clock::convertTimeOfDayToTimeval(&tod, &tv); - if (not mostRecentTv || tv > mostRecentTv.value()) { - mostRecentTv = tv; - mostRecentFile = file.path(); - } - } - if (not mostRecentFile) { - unsigned currentIdx = 0; - path pathStart = basePath / baseName; - memcpy(fileBuf.data() + currentIdx, pathStart.c_str(), pathStart.string().length()); - currentIdx += pathStart.string().length(); - Clock::TimeOfDay_t tod; - Clock::convertTimevalToTimeOfDay(¤tTv, &tod); - currentIdx += sprintf(reinterpret_cast(fileBuf.data() + currentIdx), - "_%04" PRIu32 "-%02" PRIu32 "-%02" PRIu32 "T%02" PRIu32 "-%02" PRIu32 - "-%02" PRIu32 "Z.bin", - tod.year, tod.month, tod.day, tod.hour, tod.minute, tod.second); - path newPath(std::string(reinterpret_cast(fileBuf.data()), currentIdx)); - std::ofstream of(newPath, std::ios::binary); - mostRecentFile = newPath; - mostRecentTv = currentTv; - } -} - void PersistentTmStore::addApid(uint16_t apid) { if (not filter.apid) { filter.apid = std::vector({apid}); @@ -234,19 +229,17 @@ void PersistentTmStore::addServiceSubservice(uint8_t service, uint8_t subservice void PersistentTmStore::deleteUpTo(uint32_t unixSeconds) { using namespace std::filesystem; for (auto const& file : directory_iterator(basePath)) { - if (file.is_directory() or - (mostRecentFile.has_value() and (mostRecentFile.value() == file.path()))) { + if (file.is_directory() or (activeFile.has_value() and (activeFile.value() == file.path()))) { continue; } - Clock::TimeOfDay_t tod; - pathToTod(file.path(), tod); - timeval time{}; - ReturnValue_t result = Clock::convertTimeOfDayToTimeval(&tod, &time); - if (result != returnvalue::OK) { - sif::error << "TOD to time conversion failed for file " << file << std::endl; + // Convert file time to the UNIX epoch + struct tm fileTime {}; + if (pathToTm(file.path(), fileTime) != returnvalue::OK) { + sif::error << "Time extraction for " << file << "failed" << std::endl; continue; } - if (time.tv_sec + rolloverDiffSeconds < unixSeconds) { + time_t epoch = timegm(&fileTime); + if (epoch + rolloverDiffSeconds < unixSeconds) { std::filesystem::remove(file.path()); } } @@ -259,33 +252,26 @@ void PersistentTmStore::dumpFromUpTo(uint32_t fromUnixSeconds, uint32_t upToUnix if (file.is_directory()) { continue; } - if (mostRecentFile.has_value() and mostRecentTv.has_value() and - (file.path() == mostRecentFile.value()) and - (upToUnixSeconds < static_cast(mostRecentTv.value().tv_sec))) { + struct tm fileTime {}; + if (pathToTm(file.path(), fileTime) != returnvalue::OK) { + sif::error << "Time extraction for file " << file << "failed" << std::endl; continue; } - Clock::TimeOfDay_t tod; - pathToTod(file.path(), tod); - timeval time{}; - ReturnValue_t result = Clock::convertTimeOfDayToTimeval(&tod, &time); - if (result != returnvalue::OK) { - sif::error << "TOD to time conversion failed for file " << file << std::endl; - continue; - } - auto timeUnsigned = static_cast(time.tv_sec); - if (timeUnsigned > fromUnixSeconds && timeUnsigned + rolloverDiffSeconds < upToUnixSeconds) { - fileToPackets(file, timeUnsigned, funnel); + auto fileEpoch = static_cast(timegm(&fileTime)); + if ((fileEpoch > fromUnixSeconds) and (fileEpoch + rolloverDiffSeconds <= upToUnixSeconds)) { + fileToPackets(file, fileEpoch, funnel); } } } -void PersistentTmStore::pathToTod(const std::filesystem::path& path, Clock::TimeOfDay_t& tod) { +ReturnValue_t PersistentTmStore::pathToTm(const std::filesystem::path& path, struct tm& time) { auto pathStr = path.string(); size_t splitChar = pathStr.find('_'); - auto timeOnlyStr = pathStr.substr(splitChar); - sscanf(timeOnlyStr.data(), - "%04" SCNu32 "-%02" SCNu32 "-%02" SCNu32 "T%02" SCNu32 "-%02" SCNu32 "-%02" SCNu32 "Z", - &tod.year, &tod.month, &tod.day, &tod.hour, &tod.minute, &tod.second); + auto timeOnlyStr = pathStr.substr(splitChar + 1); + if (nullptr == strptime(timeOnlyStr.c_str(), FILE_DATE_FORMAT, &time)) { + return returnvalue::FAILED; + } + return returnvalue::OK; } void PersistentTmStore::fileToPackets(const std::filesystem::path& path, uint32_t unixStamp, @@ -320,3 +306,37 @@ void PersistentTmStore::fileToPackets(const std::filesystem::path& path, uint32_ } } } + +ReturnValue_t PersistentTmStore::createMostRecentFile() { + using namespace std::filesystem; + unsigned currentIdx = 0; + path pathStart = basePath / baseName; + memcpy(fileBuf.data() + currentIdx, pathStart.c_str(), pathStart.string().length()); + currentIdx += pathStart.string().length(); + fileBuf[currentIdx] = '_'; + currentIdx += 1; + time_t epoch = currentTv.tv_sec; + struct tm* time = gmtime(&epoch); + size_t writtenBytes = strftime(reinterpret_cast(fileBuf.data() + currentIdx), + fileBuf.size(), FILE_DATE_FORMAT, time); + if (writtenBytes == 0) { + sif::error << "PersistentTmStore::createMostRecentFile: Could not create file timestamp" + << std::endl; + return returnvalue::FAILED; + } + currentIdx += writtenBytes; + strncpy(reinterpret_cast(fileBuf.data() + currentIdx), ".bin", + fileBuf.size() - currentIdx); + currentIdx += 4; + + path newPath(std::string(reinterpret_cast(fileBuf.data()), currentIdx)); + std::ofstream of(newPath, std::ios::binary); + activeFile = newPath; + activeFileTv = currentTv; + return returnvalue::OK; +} + +ReturnValue_t PersistentTmStore::initializeTmStore() { + updateBaseDir(); + return assignAndOrCreateMostRecentFile(); +} diff --git a/mission/tmtc/PersistentTmStore.h b/mission/tmtc/PersistentTmStore.h index 4285d146..bcd83a71 100644 --- a/mission/tmtc/PersistentTmStore.h +++ b/mission/tmtc/PersistentTmStore.h @@ -34,6 +34,7 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject { RolloverInterval intervalUnit, uint32_t intervalCount, timeval& currentTv, StorageManagerIF& tmStore, SdCardMountedIF& sdcMan); + ReturnValue_t initializeTmStore(); ReturnValue_t handleCommandQueue(StorageManagerIF& ipcStore, TmFunnelBase& tmFunnel); void addApid(uint16_t apid); @@ -44,11 +45,12 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject { void dumpFrom(uint32_t fromUnixSeconds, TmFunnelBase& tmFunnel); void dumpFromUpTo(uint32_t fromUnixSeconds, uint32_t upToUnixSeconds, TmFunnelBase& tmFunnel); - void updateBaseDir(); ReturnValue_t passPacket(PusTmReader& reader); private: static constexpr size_t MAX_FILESIZE = 8192; + // ISO8601 timestamp. + static constexpr char FILE_DATE_FORMAT[] = "%FT%H%M%SZ"; MessageQueueIF* tcQueue; PacketFilter filter; @@ -60,8 +62,8 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject { uint32_t rolloverDiffSeconds = 0; std::array fileBuf{}; timeval& currentTv; - std::optional mostRecentTv; - std::optional mostRecentFile; + timeval activeFileTv{}; + std::optional activeFile; SdCardMountedIF& sdcMan; StorageManagerIF& tmStore; @@ -72,9 +74,11 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject { [[nodiscard]] MessageQueueId_t getCommandQueue() const override; void calcDiffSeconds(RolloverInterval intervalUnit, uint32_t intervalCount); - void assignAndOrCreateMostRecentFile(); - static void pathToTod(const std::filesystem::path& path, Clock::TimeOfDay_t& tod); + ReturnValue_t createMostRecentFile(); + static ReturnValue_t pathToTm(const std::filesystem::path& path, struct tm& time); void fileToPackets(const std::filesystem::path& path, uint32_t unixStamp, TmFunnelBase& funnel); + void updateBaseDir(); + ReturnValue_t assignAndOrCreateMostRecentFile(); ReturnValue_t storePacket(PusTmReader& reader); }; diff --git a/mission/tmtc/PusTmFunnel.cpp b/mission/tmtc/PusTmFunnel.cpp index 2b61e19a..db645042 100644 --- a/mission/tmtc/PusTmFunnel.cpp +++ b/mission/tmtc/PusTmFunnel.cpp @@ -29,9 +29,9 @@ PusTmFunnel::PusTmFunnel(TmFunnelBase::FunnelCfg cfg, TimeReaderIF &timeReader, miscStore.addService(17); miscStore.addService(2); miscStore.addService(200); + miscStore.addService(201); okStore.addApid(config::EIVE_PUS_APID); okStore.addServiceSubservice(5, 1); - okStore.addApid(config::EIVE_PUS_APID); okStore.addService(8); okStore.addServiceSubservice(1, 1); okStore.addServiceSubservice(1, 3); @@ -51,21 +51,27 @@ PusTmFunnel::~PusTmFunnel() = default; ReturnValue_t PusTmFunnel::performOperation(uint8_t) { CommandMessage cmdMessage; - ReturnValue_t result = okStore.handleCommandQueue(ipcStore, *this); - if (result != returnvalue::OK) { - sif::error << "PusTmFunnel::performOperation: Issue handling OK store command" << std::endl; - } - result = notOkStore.handleCommandQueue(ipcStore, *this); - if (result != returnvalue::OK) { - sif::error << "PusTmFunnel::performOperation: Issue handling NOT OK store command" << std::endl; - } - result = hkStore.handleCommandQueue(ipcStore, *this); - if (result != returnvalue::OK) { - sif::error << "PusTmFunnel::performOperation: Issue handling HK store command" << std::endl; - } - result = miscStore.handleCommandQueue(ipcStore, *this); - if (result != returnvalue::OK) { - sif::error << "PusTmFunnel::performOperation: Issue handling MISC store command" << std::endl; + ReturnValue_t result; + try { + result = okStore.handleCommandQueue(ipcStore, *this); + if (result != returnvalue::OK) { + sif::error << "PusTmFunnel::performOperation: Issue handling OK store command" << std::endl; + } + result = notOkStore.handleCommandQueue(ipcStore, *this); + if (result != returnvalue::OK) { + sif::error << "PusTmFunnel::performOperation: Issue handling NOT OK store command" + << std::endl; + } + result = hkStore.handleCommandQueue(ipcStore, *this); + if (result != returnvalue::OK) { + sif::error << "PusTmFunnel::performOperation: Issue handling HK store command" << std::endl; + } + result = miscStore.handleCommandQueue(ipcStore, *this); + if (result != returnvalue::OK) { + sif::error << "PusTmFunnel::performOperation: Issue handling MISC store command" << std::endl; + } + } catch (const std::bad_optional_access &e) { + sif::error << e.what() << "when handling TM store command" << std::endl; } TmTcMessage currentMessage; @@ -134,10 +140,10 @@ const char *PusTmFunnel::getName() const { return "PUS TM Funnel"; } void PusTmFunnel::initStoresIfPossible(bool sdCardUsable) { if (not storesInitialized and sdCardUsable) { - miscStore.updateBaseDir(); - okStore.updateBaseDir(); - hkStore.updateBaseDir(); - notOkStore.updateBaseDir(); + miscStore.initializeTmStore(); + okStore.initializeTmStore(); + hkStore.initializeTmStore(); + notOkStore.initializeTmStore(); storesInitialized = true; } } diff --git a/mission/tmtc/Service15TmStorage.cpp b/mission/tmtc/Service15TmStorage.cpp index 9864b566..d61d587d 100644 --- a/mission/tmtc/Service15TmStorage.cpp +++ b/mission/tmtc/Service15TmStorage.cpp @@ -46,27 +46,33 @@ ReturnValue_t Service15TmStorage::prepareCommand(CommandMessage *message, uint8_ const uint8_t *tcData, size_t tcDataLen, uint32_t *state, object_id_t objectId) { if (subservice == Subservices::START_BY_TIME_RANGE_RETRIEVAL) { + // TODO: Hardcoded to UNIX timestamps.. Should allow arbitrary timestamp and let receiver + // to time reading and reply handling if (tcDataLen != 12) { return INVALID_TC; } store_address_t storeId; - ReturnValue_t result = ipcStore->addData(&storeId, tcData, tcDataLen); + ReturnValue_t result = ipcStore->addData(&storeId, tcData + 4, tcDataLen - 4); if (result != OK) { return result; } // Store timestamps TmStoreMessage::setDownlinkContentTimeMessage(message, storeId); + return CommandingServiceBase::EXECUTION_COMPLETE; } else if (subservice == Subservices::DELETE_UP_TO) { + // TODO: Hardcoded to UNIX timestamps.. Should allow arbitrary timestamp and let receiver + // to time reading and reply handling if (tcDataLen != 8) { return INVALID_TC; } store_address_t storeId; - ReturnValue_t result = ipcStore->addData(&storeId, tcData, tcDataLen); + ReturnValue_t result = ipcStore->addData(&storeId, tcData + 4, tcDataLen - 4); if (result != OK) { return result; } // Store timestamps TmStoreMessage::setDeleteContentTimeMessage(message, storeId); + return CommandingServiceBase::EXECUTION_COMPLETE; } return OK; } diff --git a/tmtc b/tmtc index 4917ddbb..fb676dc9 160000 --- a/tmtc +++ b/tmtc @@ -1 +1 @@ -Subproject commit 4917ddbbe4abd03ca7c7bb4ca5c80970e9b3b6bf +Subproject commit fb676dc90fe77585959297c36c828a7e852637c8