diff --git a/CHANGELOG.md b/CHANGELOG.md index fb62b8fc..bc07719d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,11 @@ will consitute of a breaking change warranting a new major release: # [unreleased] +## Added + +- Sequence counters for PUS and CFDP packets are now stored persistently across graceful reboots. +- The PUS packet message type counter will now be increment properly. + # [v4.0.1] 2023-06-24 ## Fixed diff --git a/common/config/eive/definitions.h b/common/config/eive/definitions.h index 2083962a..b369f512 100644 --- a/common/config/eive/definitions.h +++ b/common/config/eive/definitions.h @@ -11,6 +11,8 @@ static constexpr char SD_1_MOUNT_POINT[] = "/mnt/sd1"; static constexpr char OBSW_UPDATE_ARCHIVE_FILE_NAME[] = "eive-sw-update.tar.xz"; static constexpr char STRIPPED_OBSW_BINARY_FILE_NAME[] = "eive-obsw-stripped"; static constexpr char OBSW_VERSION_FILE_NAME[] = "obsw_version.txt"; +static constexpr char PUS_SEQUENCE_COUNT_FILE[] = "pus-sequence-count.txt"; +static constexpr char CFDP_SEQUENCE_COUNT_FILE[] = "cfdp-sequence-count.txt"; static constexpr char OBSW_PATH[] = "/usr/bin/eive-obsw"; static constexpr char OBSW_VERSION_FILE_PATH[] = "/usr/share/eive-obsw/obsw_version.txt"; diff --git a/fsfw b/fsfw index 0f76cdb3..c3572e31 160000 --- a/fsfw +++ b/fsfw @@ -1 +1 @@ -Subproject commit 0f76cdb3ba54f5e90a8eee4316c49cf0f581f996 +Subproject commit c3572e31a8d6845442cf8e54802d94477b2db64e diff --git a/mission/genericFactory.cpp b/mission/genericFactory.cpp index cbe78c7f..7101ab3d 100644 --- a/mission/genericFactory.cpp +++ b/mission/genericFactory.cpp @@ -91,6 +91,8 @@ EiveFaultHandler EIVE_FAULT_HANDLER; } // namespace cfdp std::atomic_bool tcs::TCS_BOARD_SHORTLY_UNAVAILABLE = false; +std::atomic_bool core::SAVE_PUS_SEQUENCE_COUNT = false; +std::atomic_bool core::SAVE_CFDP_SEQUENCE_COUNT = false; void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFunnel** pusFunnel, CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan, @@ -155,9 +157,11 @@ void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFun new PusDistributor(config::EIVE_PUS_APID, objects::PUS_PACKET_DISTRIBUTOR, ccsdsDistrib); PusTmFunnel::FunnelCfg pusFunnelCfg(objects::PUS_TM_FUNNEL, "PusTmFunnel", **tmStore, **ipcStore, - config::MAX_PUS_FUNNEL_QUEUE_DEPTH); + config::MAX_PUS_FUNNEL_QUEUE_DEPTH, sdcMan, + config::PUS_SEQUENCE_COUNT_FILE, + core::SAVE_PUS_SEQUENCE_COUNT); // The PUS funnel routes all live TM to the live destinations and to the TM stores. - *pusFunnel = new PusTmFunnel(pusFunnelCfg, *ramToFileStore, *timeStamper, sdcMan); + *pusFunnel = new PusTmFunnel(pusFunnelCfg, *ramToFileStore, *timeStamper); // MISC store and PUS funnel to MISC store routing { @@ -216,7 +220,9 @@ void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFun stores.cfdpStore->getReportReceptionQueue(0)); } PusTmFunnel::FunnelCfg cfdpFunnelCfg(objects::CFDP_TM_FUNNEL, "CfdpTmFunnel", **tmStore, - **ipcStore, config::MAX_CFDP_FUNNEL_QUEUE_DEPTH); + **ipcStore, config::MAX_CFDP_FUNNEL_QUEUE_DEPTH, sdcMan, + config::CFDP_SEQUENCE_COUNT_FILE, + core::SAVE_CFDP_SEQUENCE_COUNT); *cfdpFunnel = new CfdpTmFunnel(cfdpFunnelCfg, stores.cfdpStore->getReportReceptionQueue(0), *ramToFileStore, config::EIVE_CFDP_APID); diff --git a/mission/sysDefs.h b/mission/sysDefs.h index 32ce9642..1184a8a8 100644 --- a/mission/sysDefs.h +++ b/mission/sysDefs.h @@ -34,6 +34,9 @@ enum Copy : int { COPY_0, COPY_1, NO_COPY, SELF_COPY, ALL_COPY }; namespace core { +extern std::atomic_bool SAVE_PUS_SEQUENCE_COUNT; +extern std::atomic_bool SAVE_CFDP_SEQUENCE_COUNT; + // TODO: Support for status? Or maybe some command to quickly get information whether a unit // is running. enum SystemctlCmd : uint8_t { START = 0, STOP = 1, RESTART = 2, NUM_CMDS = 3 }; diff --git a/mission/tmtc/CfdpTmFunnel.cpp b/mission/tmtc/CfdpTmFunnel.cpp index 46915b82..93c294a4 100644 --- a/mission/tmtc/CfdpTmFunnel.cpp +++ b/mission/tmtc/CfdpTmFunnel.cpp @@ -15,8 +15,16 @@ const char* CfdpTmFunnel::getName() const { return "CFDP TM Funnel"; } ReturnValue_t CfdpTmFunnel::performOperation(uint8_t) { TmTcMessage currentMessage; + ReturnValue_t status; unsigned int count = 0; - ReturnValue_t status = tmQueue->receiveMessage(¤tMessage); + if (saveSequenceCount) { + status = saveSequenceCountToFile(); + if (status != returnvalue::OK) { + sif::error << "CfdpTmFunnel: Storing sequence count to file has failed" << std::endl; + } + saveSequenceCount = false; + } + status = tmQueue->receiveMessage(¤tMessage); while (status == returnvalue::OK) { status = handlePacket(currentMessage); if (status != returnvalue::OK) { diff --git a/mission/tmtc/CfdpTmFunnel.h b/mission/tmtc/CfdpTmFunnel.h index a4d1bc70..7b9efd34 100644 --- a/mission/tmtc/CfdpTmFunnel.h +++ b/mission/tmtc/CfdpTmFunnel.h @@ -3,6 +3,7 @@ #include +#include #include #include "fsfw/objectmanager/SystemObject.h" @@ -23,7 +24,6 @@ class CfdpTmFunnel : public TmFunnelBase { MessageQueueId_t fileStoreDest; StorageManagerIF& ramToFileStore; - uint16_t sourceSequenceCount = 0; uint16_t cfdpInCcsdsApid; }; #endif // FSFW_EXAMPLE_COMMON_CFDPTMFUNNEL_H diff --git a/mission/tmtc/PusTmFunnel.cpp b/mission/tmtc/PusTmFunnel.cpp index dbc8f1c4..b49d19d3 100644 --- a/mission/tmtc/PusTmFunnel.cpp +++ b/mission/tmtc/PusTmFunnel.cpp @@ -1,5 +1,7 @@ #include "PusTmFunnel.h" +#include + #include "eive/definitions.h" #include "eive/objects.h" #include "fsfw/ipc/CommandMessage.h" @@ -11,8 +13,8 @@ #include "tmtc/pusIds.h" PusTmFunnel::PusTmFunnel(TmFunnelBase::FunnelCfg cfg, StorageManagerIF &ramToFileStore, - TimeReaderIF &timeReader, SdCardMountedIF &sdcMan) - : TmFunnelBase(cfg), ramToFileStore(ramToFileStore), timeReader(timeReader), sdcMan(sdcMan) {} + TimeReaderIF &timeReader) + : TmFunnelBase(cfg), ramToFileStore(ramToFileStore), timeReader(timeReader) {} PusTmFunnel::~PusTmFunnel() = default; @@ -21,6 +23,13 @@ ReturnValue_t PusTmFunnel::performOperation(uint8_t) { ReturnValue_t result; TmTcMessage currentMessage; unsigned int count = 0; + if (saveSequenceCount) { + result = saveSequenceCountToFile(); + if (result != returnvalue::OK) { + sif::error << "PusTmFunnel: Storing sequence count to file has failed" << std::endl; + } + saveSequenceCount = false; + } result = tmQueue->receiveMessage(¤tMessage); while (result == returnvalue::OK) { result = handleTmPacket(currentMessage); @@ -61,6 +70,17 @@ ReturnValue_t PusTmFunnel::handleTmPacket(TmTcMessage &message) { packet.setSequenceCount(sourceSequenceCount++); sourceSequenceCount = sourceSequenceCount % ccsds::LIMIT_SEQUENCE_COUNT; packet.updateErrorControl(); + uint8_t service = packet.getMessageTypeCounter(); + auto mapIter = msgCounterMap.find(service); + if (mapIter == msgCounterMap.end()) { + msgCounterMap.emplace(service, 0); + } + if (mapIter->second == std::numeric_limits::max()) { + mapIter->second = 0; + } else { + mapIter->second++; + } + packet.setMessageCount(mapIter->second); // Send to persistent TM store if the packet matches some filter. MessageQueueId_t destination; diff --git a/mission/tmtc/PusTmFunnel.h b/mission/tmtc/PusTmFunnel.h index ba6e8d3e..3696fdcc 100644 --- a/mission/tmtc/PusTmFunnel.h +++ b/mission/tmtc/PusTmFunnel.h @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include "PersistentTmStore.h" @@ -25,7 +27,7 @@ class PusTmFunnel : public TmFunnelBase { public: PusTmFunnel(TmFunnelBase::FunnelCfg cfg, StorageManagerIF &ramToFileStore, - TimeReaderIF &timeReader, SdCardMountedIF &sdcMan); + TimeReaderIF &timeReader); [[nodiscard]] const char *getName() const override; ~PusTmFunnel() override; @@ -36,11 +38,10 @@ class PusTmFunnel : public TmFunnelBase { // Update TV stamp every 5 minutes static constexpr dur_millis_t TV_UPDATE_INTERVAL_SECS = 60 * 5; - uint16_t sourceSequenceCount = 0; + std::map msgCounterMap; StorageManagerIF &ramToFileStore; TimeReaderIF &timeReader; bool storesInitialized = false; - SdCardMountedIF &sdcMan; PusTmRouteByFilterHelper persistentTmMap; ReturnValue_t handleTmPacket(TmTcMessage &message); diff --git a/mission/tmtc/TmFunnelBase.cpp b/mission/tmtc/TmFunnelBase.cpp index eb480b03..16e4d3bb 100644 --- a/mission/tmtc/TmFunnelBase.cpp +++ b/mission/tmtc/TmFunnelBase.cpp @@ -2,6 +2,10 @@ #include +#include +#include +#include + #include "fsfw/ipc/QueueFactory.h" TmFunnelBase::TmFunnelBase(FunnelCfg cfg) @@ -10,7 +14,10 @@ TmFunnelBase::TmFunnelBase(FunnelCfg cfg) tmStore(cfg.tmStore), ipcStore(cfg.ipcStore), tmQueue(QueueFactory::instance()->createMessageQueue(cfg.tmMsgDepth)), - liveDemux(*tmQueue) {} + liveDemux(*tmQueue), + sdcMan(cfg.sdcMan), + sequenceCounterFilename(cfg.sequenceCounterFilename), + saveSequenceCount(cfg.saveSequenceCount) {} ReturnValue_t TmFunnelBase::demultiplexLivePackets(store_address_t origStoreId, const uint8_t *tmData, size_t tmSize) { @@ -27,3 +34,38 @@ void TmFunnelBase::addLiveDestination(const char *name, const AcceptsTelemetryIF &downlinkDestination, uint8_t vcid) { liveDemux.addDestination(name, downlinkDestination, vcid); } + +ReturnValue_t TmFunnelBase::initialize() { + using namespace std::filesystem; + // The filesystem should always be available at the start.. Let's assume it always is, otherwise + // we just live with a regular 0 initialization. It simplifies a lot. + std::error_code e; + path filePath = + path(sdcMan.getCurrentMountPrefix() / std::string("conf") / sequenceCounterFilename); + if (exists(filePath, e)) { + std::ifstream ifile(filePath); + if (ifile.bad()) { + sif::error << "TmFunnelBase::initialize: Faulty file open for sequence counter initialization" + << std::endl; + return returnvalue::OK; + } + if (not(ifile >> sourceSequenceCount)) { + // Initialize to 0 in any case if reading a number failed. + sourceSequenceCount = 0; + } + } + return returnvalue::OK; +} + +ReturnValue_t TmFunnelBase::saveSequenceCountToFile() { + using namespace std::filesystem; + std::error_code e; + path filePath = + path(sdcMan.getCurrentMountPrefix() / std::string("conf") / sequenceCounterFilename); + std::ofstream ofile(filePath); + if (ofile.bad()) { + return returnvalue::FAILED; + } + ofile << sourceSequenceCount << "\n"; + return returnvalue::OK; +} diff --git a/mission/tmtc/TmFunnelBase.h b/mission/tmtc/TmFunnelBase.h index 51d16626..72d91103 100644 --- a/mission/tmtc/TmFunnelBase.h +++ b/mission/tmtc/TmFunnelBase.h @@ -6,25 +6,34 @@ #include #include #include +#include #include +#include #include class TmFunnelBase : public AcceptsTelemetryIF, public SystemObject { public: struct FunnelCfg { FunnelCfg(object_id_t objId, const char* name, StorageManagerIF& tmStore, - StorageManagerIF& ipcStore, uint32_t tmMsgDepth) + StorageManagerIF& ipcStore, uint32_t tmMsgDepth, SdCardMountedIF& sdcMan, + const char* sequenceCounterFilename, std::atomic_bool& saveSequenceCount) : objectId(objId), name(name), tmStore(tmStore), ipcStore(ipcStore), - tmMsgDepth(tmMsgDepth) {} + tmMsgDepth(tmMsgDepth), + sdcMan(sdcMan), + sequenceCounterFilename(sequenceCounterFilename), + saveSequenceCount(saveSequenceCount) {} object_id_t objectId; const char* name; StorageManagerIF& tmStore; StorageManagerIF& ipcStore; uint32_t tmMsgDepth; + SdCardMountedIF& sdcMan; + const char* sequenceCounterFilename; + std::atomic_bool& saveSequenceCount; }; explicit TmFunnelBase(FunnelCfg cfg); [[nodiscard]] MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel) const override; @@ -32,6 +41,9 @@ class TmFunnelBase : public AcceptsTelemetryIF, public SystemObject { uint8_t vcid = 0); ReturnValue_t demultiplexLivePackets(store_address_t origStoreId, const uint8_t* tmData, size_t tmSize); + ReturnValue_t initialize() override; + + ReturnValue_t saveSequenceCountToFile(); ~TmFunnelBase() override; @@ -41,6 +53,10 @@ class TmFunnelBase : public AcceptsTelemetryIF, public SystemObject { StorageManagerIF& ipcStore; MessageQueueIF* tmQueue = nullptr; PusLiveDemux liveDemux; + SdCardMountedIF& sdcMan; + const char* sequenceCounterFilename; + std::atomic_bool& saveSequenceCount; + uint16_t sourceSequenceCount = 0; }; #endif /* MISSION_TMTC_TMFUNNELBASE_H_ */