Merge pull request 'Seq Count Persistent + MSG type counter' (#711) from sequence-counter-persistent-msg-type-count-support into main
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good

Reviewed-on: #711
Reviewed-by: Marius Eggert <eggertm@irs.uni-stuttgart.de>
This commit is contained in:
Robin Müller 2023-06-28 13:37:14 +02:00
commit 0cdb850f69
13 changed files with 138 additions and 16 deletions

View File

@ -24,7 +24,6 @@ will consitute of a breaking change warranting a new major release:
## Changed ## Changed
- Internal error reporter set is now enabled by default and generated every 120 seconds.
- Persistent TM store dumps are now performed in chronological order. - Persistent TM store dumps are now performed in chronological order.
- Increase Syrlinks RX HK rate to 5.0 seconds during a pass. - Increase Syrlinks RX HK rate to 5.0 seconds during a pass.
- Various robustness improvements for the heater handler. The heater handler will now only - Various robustness improvements for the heater handler. The heater handler will now only
@ -34,6 +33,12 @@ will consitute of a breaking change warranting a new major release:
twice in a 0.5 second slot so something like a consecutive heater ON or OFF command can still twice in a 0.5 second slot so something like a consecutive heater ON or OFF command can still
be handled relatively quickly. be handled relatively quickly.
## Added
- Sequence counters for PUS and CFDP packets are now stored persistently across graceful reboots.
- The PUS packet message type counter will now be incremented properly for each PUS service.
- Internal error reporter set is now enabled by default and generated every 120 seconds.
# [v5.0.0] 2023-06-26 # [v5.0.0] 2023-06-26
v3.3.1 and all following version will now be moved to v5.0.0 with the additional changes listed v3.3.1 and all following version will now be moved to v5.0.0 with the additional changes listed

View File

@ -1224,6 +1224,10 @@ ReturnValue_t CoreController::actionReboot(const uint8_t *data, size_t size) {
ReturnValue_t CoreController::gracefulShutdownTasks(xsc::Chip chip, xsc::Copy copy, ReturnValue_t CoreController::gracefulShutdownTasks(xsc::Chip chip, xsc::Copy copy,
bool &protOpPerformed) { bool &protOpPerformed) {
// Store both sequence counters persistently.
core::SAVE_CFDP_SEQUENCE_COUNT = true;
core::SAVE_PUS_SEQUENCE_COUNT = true;
sdcMan->setBlocking(true); sdcMan->setBlocking(true);
sdcMan->markUnusable(); sdcMan->markUnusable();
// Wait two seconds to ensure no one uses the SD cards // Wait two seconds to ensure no one uses the SD cards

View File

@ -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 OBSW_UPDATE_ARCHIVE_FILE_NAME[] = "eive-sw-update.tar.xz";
static constexpr char STRIPPED_OBSW_BINARY_FILE_NAME[] = "eive-obsw-stripped"; static constexpr char STRIPPED_OBSW_BINARY_FILE_NAME[] = "eive-obsw-stripped";
static constexpr char OBSW_VERSION_FILE_NAME[] = "obsw_version.txt"; 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_PATH[] = "/usr/bin/eive-obsw";
static constexpr char OBSW_VERSION_FILE_PATH[] = "/usr/share/eive-obsw/obsw_version.txt"; static constexpr char OBSW_VERSION_FILE_PATH[] = "/usr/share/eive-obsw/obsw_version.txt";

2
fsfw

@ -1 +1 @@
Subproject commit 6aff3250c29f5243eb4a6111ba0a5c0cc1a3033c Subproject commit 8da89eba80f73cb05e5c38fc012456f1d9569af5

View File

@ -91,6 +91,8 @@ EiveFaultHandler EIVE_FAULT_HANDLER;
} // namespace cfdp } // namespace cfdp
std::atomic_bool tcs::TCS_BOARD_SHORTLY_UNAVAILABLE = false; 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, void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFunnel** pusFunnel,
CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan, 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); new PusDistributor(config::EIVE_PUS_APID, objects::PUS_PACKET_DISTRIBUTOR, ccsdsDistrib);
PusTmFunnel::FunnelCfg pusFunnelCfg(objects::PUS_TM_FUNNEL, "PusTmFunnel", **tmStore, **ipcStore, 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. // 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 // MISC store and PUS funnel to MISC store routing
{ {
@ -216,7 +220,9 @@ void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFun
stores.cfdpStore->getReportReceptionQueue(0)); stores.cfdpStore->getReportReceptionQueue(0));
} }
PusTmFunnel::FunnelCfg cfdpFunnelCfg(objects::CFDP_TM_FUNNEL, "CfdpTmFunnel", **tmStore, 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), *cfdpFunnel = new CfdpTmFunnel(cfdpFunnelCfg, stores.cfdpStore->getReportReceptionQueue(0),
*ramToFileStore, config::EIVE_CFDP_APID); *ramToFileStore, config::EIVE_CFDP_APID);

View File

@ -34,6 +34,9 @@ enum Copy : int { COPY_0, COPY_1, NO_COPY, SELF_COPY, ALL_COPY };
namespace core { 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 // TODO: Support for status? Or maybe some command to quickly get information whether a unit
// is running. // is running.
enum SystemctlCmd : uint8_t { START = 0, STOP = 1, RESTART = 2, NUM_CMDS = 3 }; enum SystemctlCmd : uint8_t { START = 0, STOP = 1, RESTART = 2, NUM_CMDS = 3 };

View File

@ -15,8 +15,16 @@ const char* CfdpTmFunnel::getName() const { return "CFDP TM Funnel"; }
ReturnValue_t CfdpTmFunnel::performOperation(uint8_t) { ReturnValue_t CfdpTmFunnel::performOperation(uint8_t) {
TmTcMessage currentMessage; TmTcMessage currentMessage;
ReturnValue_t status;
unsigned int count = 0; unsigned int count = 0;
ReturnValue_t status = tmQueue->receiveMessage(&currentMessage); 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(&currentMessage);
while (status == returnvalue::OK) { while (status == returnvalue::OK) {
status = handlePacket(currentMessage); status = handlePacket(currentMessage);
if (status != returnvalue::OK) { if (status != returnvalue::OK) {

View File

@ -3,6 +3,7 @@
#include <mission/tmtc/TmFunnelBase.h> #include <mission/tmtc/TmFunnelBase.h>
#include <atomic>
#include <vector> #include <vector>
#include "fsfw/objectmanager/SystemObject.h" #include "fsfw/objectmanager/SystemObject.h"
@ -23,7 +24,6 @@ class CfdpTmFunnel : public TmFunnelBase {
MessageQueueId_t fileStoreDest; MessageQueueId_t fileStoreDest;
StorageManagerIF& ramToFileStore; StorageManagerIF& ramToFileStore;
uint16_t sourceSequenceCount = 0;
uint16_t cfdpInCcsdsApid; uint16_t cfdpInCcsdsApid;
}; };
#endif // FSFW_EXAMPLE_COMMON_CFDPTMFUNNEL_H #endif // FSFW_EXAMPLE_COMMON_CFDPTMFUNNEL_H

View File

@ -1,5 +1,7 @@
#include "PusTmFunnel.h" #include "PusTmFunnel.h"
#include <limits>
#include "eive/definitions.h" #include "eive/definitions.h"
#include "eive/objects.h" #include "eive/objects.h"
#include "fsfw/ipc/CommandMessage.h" #include "fsfw/ipc/CommandMessage.h"
@ -11,8 +13,8 @@
#include "tmtc/pusIds.h" #include "tmtc/pusIds.h"
PusTmFunnel::PusTmFunnel(TmFunnelBase::FunnelCfg cfg, StorageManagerIF &ramToFileStore, PusTmFunnel::PusTmFunnel(TmFunnelBase::FunnelCfg cfg, StorageManagerIF &ramToFileStore,
TimeReaderIF &timeReader, SdCardMountedIF &sdcMan) TimeReaderIF &timeReader)
: TmFunnelBase(cfg), ramToFileStore(ramToFileStore), timeReader(timeReader), sdcMan(sdcMan) {} : TmFunnelBase(cfg), ramToFileStore(ramToFileStore), timeReader(timeReader) {}
PusTmFunnel::~PusTmFunnel() = default; PusTmFunnel::~PusTmFunnel() = default;
@ -21,6 +23,13 @@ ReturnValue_t PusTmFunnel::performOperation(uint8_t) {
ReturnValue_t result; ReturnValue_t result;
TmTcMessage currentMessage; TmTcMessage currentMessage;
unsigned int count = 0; 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(&currentMessage); result = tmQueue->receiveMessage(&currentMessage);
while (result == returnvalue::OK) { while (result == returnvalue::OK) {
result = handleTmPacket(currentMessage); result = handleTmPacket(currentMessage);
@ -59,7 +68,33 @@ ReturnValue_t PusTmFunnel::handleTmPacket(TmTcMessage &message) {
return result; return result;
} }
packet.setSequenceCount(sourceSequenceCount++); packet.setSequenceCount(sourceSequenceCount++);
// NOTE: This only works because the limit value bit width is below 16 bits!
sourceSequenceCount = sourceSequenceCount % ccsds::LIMIT_SEQUENCE_COUNT; sourceSequenceCount = sourceSequenceCount % ccsds::LIMIT_SEQUENCE_COUNT;
// Message type counter handling.
uint8_t service = packet.getService();
bool insertionFailed = false;
auto mapIter = msgCounterMap.find(service);
if (mapIter == msgCounterMap.end()) {
auto iterPair = msgCounterMap.emplace(service, 0);
if (iterPair.second) {
mapIter = iterPair.first;
} else {
// Should really never never happen but you never know..
insertionFailed = true;
}
}
if (not insertionFailed) {
packet.setMessageCount(mapIter->second);
// Sane overflow handling.
if (mapIter->second == std::numeric_limits<uint16_t>::max()) {
mapIter->second = 0;
} else {
mapIter->second++;
}
}
// Re-calculate CRC after changing the fields. This operation HAS to come last!
packet.updateErrorControl(); packet.updateErrorControl();
// Send to persistent TM store if the packet matches some filter. // Send to persistent TM store if the packet matches some filter.

View File

@ -9,6 +9,8 @@
#include <mission/tmtc/PusTmRouteByFilterHelper.h> #include <mission/tmtc/PusTmRouteByFilterHelper.h>
#include <mission/tmtc/TmFunnelBase.h> #include <mission/tmtc/TmFunnelBase.h>
#include <atomic>
#include <map>
#include <vector> #include <vector>
#include "PersistentTmStore.h" #include "PersistentTmStore.h"
@ -25,7 +27,7 @@
class PusTmFunnel : public TmFunnelBase { class PusTmFunnel : public TmFunnelBase {
public: public:
PusTmFunnel(TmFunnelBase::FunnelCfg cfg, StorageManagerIF &ramToFileStore, PusTmFunnel(TmFunnelBase::FunnelCfg cfg, StorageManagerIF &ramToFileStore,
TimeReaderIF &timeReader, SdCardMountedIF &sdcMan); TimeReaderIF &timeReader);
[[nodiscard]] const char *getName() const override; [[nodiscard]] const char *getName() const override;
~PusTmFunnel() override; ~PusTmFunnel() override;
@ -36,11 +38,10 @@ class PusTmFunnel : public TmFunnelBase {
// Update TV stamp every 5 minutes // Update TV stamp every 5 minutes
static constexpr dur_millis_t TV_UPDATE_INTERVAL_SECS = 60 * 5; static constexpr dur_millis_t TV_UPDATE_INTERVAL_SECS = 60 * 5;
uint16_t sourceSequenceCount = 0; std::map<uint8_t, uint16_t> msgCounterMap;
StorageManagerIF &ramToFileStore; StorageManagerIF &ramToFileStore;
TimeReaderIF &timeReader; TimeReaderIF &timeReader;
bool storesInitialized = false; bool storesInitialized = false;
SdCardMountedIF &sdcMan;
PusTmRouteByFilterHelper persistentTmMap; PusTmRouteByFilterHelper persistentTmMap;
ReturnValue_t handleTmPacket(TmTcMessage &message); ReturnValue_t handleTmPacket(TmTcMessage &message);

View File

@ -2,6 +2,10 @@
#include <fsfw/tmtcservices/TmTcMessage.h> #include <fsfw/tmtcservices/TmTcMessage.h>
#include <atomic>
#include <filesystem>
#include <fstream>
#include "fsfw/ipc/QueueFactory.h" #include "fsfw/ipc/QueueFactory.h"
TmFunnelBase::TmFunnelBase(FunnelCfg cfg) TmFunnelBase::TmFunnelBase(FunnelCfg cfg)
@ -10,7 +14,10 @@ TmFunnelBase::TmFunnelBase(FunnelCfg cfg)
tmStore(cfg.tmStore), tmStore(cfg.tmStore),
ipcStore(cfg.ipcStore), ipcStore(cfg.ipcStore),
tmQueue(QueueFactory::instance()->createMessageQueue(cfg.tmMsgDepth)), 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, ReturnValue_t TmFunnelBase::demultiplexLivePackets(store_address_t origStoreId,
const uint8_t *tmData, size_t tmSize) { const uint8_t *tmData, size_t tmSize) {
@ -27,3 +34,38 @@ void TmFunnelBase::addLiveDestination(const char *name,
const AcceptsTelemetryIF &downlinkDestination, uint8_t vcid) { const AcceptsTelemetryIF &downlinkDestination, uint8_t vcid) {
liveDemux.addDestination(name, downlinkDestination, 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(path(sdcMan.getCurrentMountPrefix()) / path("conf") / path(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(path(sdcMan.getCurrentMountPrefix()) / path("conf") / path(sequenceCounterFilename));
std::ofstream ofile(filePath);
if (ofile.bad()) {
return returnvalue::FAILED;
}
ofile << sourceSequenceCount << "\n";
return returnvalue::OK;
}

View File

@ -6,25 +6,34 @@
#include <fsfw/tmstorage/TmStoreFrontendSimpleIF.h> #include <fsfw/tmstorage/TmStoreFrontendSimpleIF.h>
#include <fsfw/tmtcservices/AcceptsTelemetryIF.h> #include <fsfw/tmtcservices/AcceptsTelemetryIF.h>
#include <fsfw/tmtcservices/TmTcMessage.h> #include <fsfw/tmtcservices/TmTcMessage.h>
#include <mission/memory/SdCardMountedIF.h>
#include <mission/tmtc/PusLiveDemux.h> #include <mission/tmtc/PusLiveDemux.h>
#include <atomic>
#include <vector> #include <vector>
class TmFunnelBase : public AcceptsTelemetryIF, public SystemObject { class TmFunnelBase : public AcceptsTelemetryIF, public SystemObject {
public: public:
struct FunnelCfg { struct FunnelCfg {
FunnelCfg(object_id_t objId, const char* name, StorageManagerIF& tmStore, 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), : objectId(objId),
name(name), name(name),
tmStore(tmStore), tmStore(tmStore),
ipcStore(ipcStore), ipcStore(ipcStore),
tmMsgDepth(tmMsgDepth) {} tmMsgDepth(tmMsgDepth),
sdcMan(sdcMan),
sequenceCounterFilename(sequenceCounterFilename),
saveSequenceCount(saveSequenceCount) {}
object_id_t objectId; object_id_t objectId;
const char* name; const char* name;
StorageManagerIF& tmStore; StorageManagerIF& tmStore;
StorageManagerIF& ipcStore; StorageManagerIF& ipcStore;
uint32_t tmMsgDepth; uint32_t tmMsgDepth;
SdCardMountedIF& sdcMan;
const char* sequenceCounterFilename;
std::atomic_bool& saveSequenceCount;
}; };
explicit TmFunnelBase(FunnelCfg cfg); explicit TmFunnelBase(FunnelCfg cfg);
[[nodiscard]] MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel) const override; [[nodiscard]] MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel) const override;
@ -32,6 +41,9 @@ class TmFunnelBase : public AcceptsTelemetryIF, public SystemObject {
uint8_t vcid = 0); uint8_t vcid = 0);
ReturnValue_t demultiplexLivePackets(store_address_t origStoreId, const uint8_t* tmData, ReturnValue_t demultiplexLivePackets(store_address_t origStoreId, const uint8_t* tmData,
size_t tmSize); size_t tmSize);
ReturnValue_t initialize() override;
ReturnValue_t saveSequenceCountToFile();
~TmFunnelBase() override; ~TmFunnelBase() override;
@ -41,6 +53,10 @@ class TmFunnelBase : public AcceptsTelemetryIF, public SystemObject {
StorageManagerIF& ipcStore; StorageManagerIF& ipcStore;
MessageQueueIF* tmQueue = nullptr; MessageQueueIF* tmQueue = nullptr;
PusLiveDemux liveDemux; PusLiveDemux liveDemux;
SdCardMountedIF& sdcMan;
const char* sequenceCounterFilename;
std::atomic_bool& saveSequenceCount;
uint16_t sourceSequenceCount = 0;
}; };
#endif /* MISSION_TMTC_TMFUNNELBASE_H_ */ #endif /* MISSION_TMTC_TMFUNNELBASE_H_ */

2
tmtc

@ -1 +1 @@
Subproject commit ec0ebc365308198046addc94909b1bca8678aa5a Subproject commit 8caa408cbdf7eebdd441cff580063283ab81ffe1