Merge remote-tracking branch 'origin/main' into only-reset-ptme-on-rate-change
Some checks failed
EIVE/eive-obsw/pipeline/pr-main There was a failure building this commit

This commit is contained in:
Robin Müller 2023-06-28 13:21:33 +02:00
commit b5cc430b5b
32 changed files with 982 additions and 467 deletions

View File

@ -16,6 +16,41 @@ will consitute of a breaking change warranting a new major release:
# [unreleased]
# [v6.0.0] to be released
- Important bugfixes for PTME. See `q7s-package` CHANGELOG.
# [v5.1.0] to be released
## 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.
- Various robustness improvements for the heater handler. The heater handler will now only
process the command queue if it is not busy with switch commanding which reduces the amount
of possible bugs.
- The heater handler is only able to process messages stricly sequentially now but is scheduled
twice in a 0.5 second slot so something like a consecutive heater ON or OFF command can still
be handled relatively quickly.
# [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
here. This was done because the firmware update (v4.0.0) is not working right now and it is not
known when and how it will be fixed. Because of that, all updates to make the SW work with the new
firmware, which are limited to a few files will be moved to a dev branch so regular development
compatible to the old firmware can continue.
TLDR: This version is compatible to the old firmware and some changes which only work with the new
firmware have been reverted.
## Changed
- Added `sync` syscall in graceful shutdown handler
- Graceful shutdown is now performed by the reboot watchdog
- There is now a separate file for the total reboot counter. The reboot watchdog has its own local
counters to determine whether a reboot is necessary.
# [v4.0.1] 2023-06-24
## Fixed

View File

@ -9,9 +9,9 @@
# ##############################################################################
cmake_minimum_required(VERSION 3.13)
set(OBSW_VERSION_MAJOR 4)
set(OBSW_VERSION_MAJOR 5)
set(OBSW_VERSION_MINOR 0)
set(OBSW_VERSION_REVISION 1)
set(OBSW_VERSION_REVISION 0)
# set(CMAKE_VERBOSE TRUE)

View File

@ -68,7 +68,7 @@ void ObjectFactory::produce(void* args) {
#endif
auto sdcMan = new DummySdCardManager("/tmp");
ObjectFactory::produceGenericObjects(nullptr, &pusFunnel, &cfdpFunnel, *sdcMan, &ipcStore,
&tmStore, persistentStores, 120);
&tmStore, persistentStores, 120, enableHkSets);
new TmFunnelHandler(objects::LIVE_TM_TASK, *pusFunnel, *cfdpFunnel);
auto* dummyGpioIF = new DummyGpioIF();

View File

@ -85,9 +85,13 @@ static constexpr char EN_RW_4[] = "enable_rw_4";
static constexpr char RAD_SENSOR_CHIP_SELECT[] = "rad_sensor_chip_select";
static constexpr char ENABLE_RADFET[] = "enable_radfet";
static constexpr char PAPB_BUSY_SIGNAL_VC0[] = "papb_busy_signal_vc0";
static constexpr char PAPB_EMPTY_SIGNAL_VC0[] = "papb_empty_signal_vc0";
static constexpr char PAPB_BUSY_SIGNAL_VC1[] = "papb_busy_signal_vc1";
static constexpr char PAPB_EMPTY_SIGNAL_VC1[] = "papb_empty_signal_vc1";
static constexpr char PAPB_BUSY_SIGNAL_VC2[] = "papb_busy_signal_vc2";
static constexpr char PAPB_EMPTY_SIGNAL_VC2[] = "papb_empty_signal_vc2";
static constexpr char PAPB_BUSY_SIGNAL_VC3[] = "papb_busy_signal_vc3";
static constexpr char PAPB_EMPTY_SIGNAL_VC3[] = "papb_empty_signal_vc3";
static constexpr char PTME_RESETN[] = "ptme_resetn";

View File

@ -190,6 +190,7 @@ ReturnValue_t CoreController::initialize() {
sif::warning << "Subscribing for GPS GPS_FIX_CHANGE event failed" << std::endl;
}
if (common::OBSW_VERSION_MAJOR >= 6 or common::OBSW_VERSION_MAJOR == 4) {
UioMapper sysRomMapper(q7s::UIO_SYS_ROM);
result = sysRomMapper.getMappedAdress(&mappedSysRomAddr, UioMapper::Permissions::READ_ONLY);
if (result != returnvalue::OK) {
@ -197,6 +198,7 @@ ReturnValue_t CoreController::initialize() {
sif::error << "Getting mapped SYS ROM UIO address failed" << std::endl;
return ObjectManager::CHILD_INIT_FAILED;
}
}
return returnvalue::OK;
}
@ -321,23 +323,23 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
if (size < 1) {
return HasActionsIF::INVALID_PARAMETERS;
}
std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_FILE;
parseRebootFile(path, rebootFile);
std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_WATCHDOG_FILE;
parseRebootWatchdogFile(path, rebootWatchdogFile);
if (data[0] == 0) {
rebootFile.enabled = false;
rewriteRebootFile(rebootFile);
rebootWatchdogFile.enabled = false;
rewriteRebootWatchdogFile(rebootWatchdogFile);
} else if (data[0] == 1) {
rebootFile.enabled = true;
rewriteRebootFile(rebootFile);
rebootWatchdogFile.enabled = true;
rewriteRebootWatchdogFile(rebootWatchdogFile);
} else {
return HasActionsIF::INVALID_PARAMETERS;
}
return HasActionsIF::EXECUTION_FINISHED;
}
case (READ_REBOOT_MECHANISM_INFO): {
std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_FILE;
parseRebootFile(path, rebootFile);
RebootMechanismPacket packet(rebootFile);
std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_WATCHDOG_FILE;
parseRebootWatchdogFile(path, rebootWatchdogFile);
RebootWatchdogPacket packet(rebootWatchdogFile);
ReturnValue_t result = actionHelper.reportData(commandedBy, actionId, &packet);
if (result != returnvalue::OK) {
return result;
@ -346,12 +348,13 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
}
case (RESET_REBOOT_COUNTERS): {
if (size == 0) {
resetRebootCount(xsc::ALL_CHIP, xsc::ALL_COPY);
resetRebootWatchdogCounters(xsc::ALL_CHIP, xsc::ALL_COPY);
} else if (size == 2) {
if (data[0] > 1 or data[1] > 1) {
return HasActionsIF::INVALID_PARAMETERS;
}
resetRebootCount(static_cast<xsc::Chip>(data[0]), static_cast<xsc::Copy>(data[1]));
resetRebootWatchdogCounters(static_cast<xsc::Chip>(data[0]),
static_cast<xsc::Copy>(data[1]));
}
return HasActionsIF::EXECUTION_FINISHED;
}
@ -446,11 +449,11 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
if (size < 1) {
return HasActionsIF::INVALID_PARAMETERS;
}
std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_FILE;
std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_WATCHDOG_FILE;
// Disable the reboot file mechanism
parseRebootFile(path, rebootFile);
rebootFile.maxCount = data[0];
rewriteRebootFile(rebootFile);
parseRebootWatchdogFile(path, rebootWatchdogFile);
rebootWatchdogFile.maxCount = data[0];
rewriteRebootWatchdogFile(rebootWatchdogFile);
return HasActionsIF::EXECUTION_FINISHED;
}
case (XSC_REBOOT_OBC): {
@ -1208,45 +1211,7 @@ ReturnValue_t CoreController::actionXscReboot(const uint8_t *data, size_t size)
auto tgtChip = static_cast<xsc::Chip>(data[1]);
auto tgtCopy = static_cast<xsc::Copy>(data[2]);
// This function can not really fail
gracefulShutdownTasks(tgtChip, tgtCopy, protOpPerformed);
switch (tgtChip) {
case (xsc::Chip::CHIP_0): {
switch (tgtCopy) {
case (xsc::Copy::COPY_0): {
xsc_boot_copy(XSC_LIBNOR_CHIP_0, XSC_LIBNOR_COPY_NOMINAL);
break;
}
case (xsc::Copy::COPY_1): {
xsc_boot_copy(XSC_LIBNOR_CHIP_0, XSC_LIBNOR_COPY_GOLD);
break;
}
default: {
break;
}
}
break;
}
case (xsc::Chip::CHIP_1): {
switch (tgtCopy) {
case (xsc::Copy::COPY_0): {
xsc_boot_copy(XSC_LIBNOR_CHIP_1, XSC_LIBNOR_COPY_NOMINAL);
break;
}
case (xsc::Copy::COPY_1): {
xsc_boot_copy(XSC_LIBNOR_CHIP_1, XSC_LIBNOR_COPY_GOLD);
break;
}
default: {
break;
}
}
break;
}
default:
break;
}
performGracefulShutdown(tgtChip, tgtCopy);
return returnvalue::FAILED;
}
@ -1263,10 +1228,15 @@ ReturnValue_t CoreController::gracefulShutdownTasks(xsc::Chip chip, xsc::Copy co
sdcMan->markUnusable();
// Wait two seconds to ensure no one uses the SD cards
TaskFactory::delayTask(2000);
// Ensure that all writes/reads do finish.
sync();
// Attempt graceful shutdown by unmounting and switching off SD cards
sdcMan->switchOffSdCard(sd::SdCard::SLOT_0);
sdcMan->switchOffSdCard(sd::SdCard::SLOT_1);
// If any boot copies are unprotected
// If any boot copies are unprotected.
// Actually this function only ensures that reboots to the own image are protected..
ReturnValue_t result = setBootCopyProtection(xsc::Chip::SELF_CHIP, xsc::Copy::SELF_COPY, true,
protOpPerformed, false);
if (result == returnvalue::OK and protOpPerformed) {
@ -1555,7 +1525,8 @@ void CoreController::performMountedSdCardOperations() {
if (not timeFileInitDone) {
initClockFromTimeFile();
}
performRebootFileHandling(false);
performRebootWatchdogHandling(false);
performRebootCountersHandling(false);
}
backupTimeFileHandler();
};
@ -1627,117 +1598,126 @@ ReturnValue_t CoreController::performSdCardCheck() {
return returnvalue::OK;
}
void CoreController::performRebootFileHandling(bool recreateFile) {
void CoreController::performRebootWatchdogHandling(bool recreateFile) {
using namespace std;
std::string path = currMntPrefix + REBOOT_FILE;
std::string path = currMntPrefix + REBOOT_WATCHDOG_FILE;
std::string legacyPath = currMntPrefix + LEGACY_REBOOT_WATCHDOG_FILE;
std::error_code e;
// TODO: Remove at some point in the future.
if (std::filesystem::exists(legacyPath, e)) {
// Old file might still exist, so copy it to new path
std::filesystem::copy(legacyPath, path, std::filesystem::copy_options::overwrite_existing, e);
if (e) {
sif::error << "File copy has failed: " << e.message() << std::endl;
}
}
if (not std::filesystem::exists(path, e) or recreateFile) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::info << "CoreController::performRebootFileHandling: Recreating reboot file" << std::endl;
sif::info << "CoreController::performRebootFileHandling: Recreating reboot watchdog file"
<< std::endl;
#endif
rebootFile.enabled = false;
rebootFile.img00Cnt = 0;
rebootFile.img01Cnt = 0;
rebootFile.img10Cnt = 0;
rebootFile.img11Cnt = 0;
rebootFile.lastChip = xsc::Chip::CHIP_0;
rebootFile.lastCopy = xsc::Copy::COPY_0;
rebootFile.img00Lock = false;
rebootFile.img01Lock = false;
rebootFile.img10Lock = false;
rebootFile.img11Lock = false;
rebootFile.mechanismNextChip = xsc::Chip::NO_CHIP;
rebootFile.mechanismNextCopy = xsc::Copy::NO_COPY;
rebootFile.bootFlag = false;
rewriteRebootFile(rebootFile);
rebootWatchdogFile.enabled = false;
rebootWatchdogFile.img00Cnt = 0;
rebootWatchdogFile.img01Cnt = 0;
rebootWatchdogFile.img10Cnt = 0;
rebootWatchdogFile.img11Cnt = 0;
rebootWatchdogFile.lastChip = xsc::Chip::CHIP_0;
rebootWatchdogFile.lastCopy = xsc::Copy::COPY_0;
rebootWatchdogFile.img00Lock = false;
rebootWatchdogFile.img01Lock = false;
rebootWatchdogFile.img10Lock = false;
rebootWatchdogFile.img11Lock = false;
rebootWatchdogFile.mechanismNextChip = xsc::Chip::NO_CHIP;
rebootWatchdogFile.mechanismNextCopy = xsc::Copy::NO_COPY;
rebootWatchdogFile.bootFlag = false;
rewriteRebootWatchdogFile(rebootWatchdogFile);
} else {
if (not parseRebootFile(path, rebootFile)) {
performRebootFileHandling(true);
if (not parseRebootWatchdogFile(path, rebootWatchdogFile)) {
performRebootWatchdogHandling(true);
return;
}
}
if (CURRENT_CHIP == xsc::CHIP_0) {
if (CURRENT_COPY == xsc::COPY_0) {
rebootFile.img00Cnt++;
rebootWatchdogFile.img00Cnt++;
} else {
rebootFile.img01Cnt++;
rebootWatchdogFile.img01Cnt++;
}
} else {
if (CURRENT_COPY == xsc::COPY_0) {
rebootFile.img10Cnt++;
rebootWatchdogFile.img10Cnt++;
} else {
rebootFile.img11Cnt++;
rebootWatchdogFile.img11Cnt++;
}
}
if (rebootFile.bootFlag) {
if (rebootWatchdogFile.bootFlag) {
// Trigger event to inform ground that a reboot was triggered
uint32_t p1 = rebootFile.lastChip << 16 | rebootFile.lastCopy;
uint32_t p1 = rebootWatchdogFile.lastChip << 16 | rebootWatchdogFile.lastCopy;
triggerEvent(core::REBOOT_MECHANISM_TRIGGERED, p1, 0);
// Clear the boot flag
rebootFile.bootFlag = false;
rebootWatchdogFile.bootFlag = false;
}
announceBootCounts();
if (rebootFile.mechanismNextChip != xsc::NO_CHIP and
rebootFile.mechanismNextCopy != xsc::NO_COPY) {
if (CURRENT_CHIP != rebootFile.mechanismNextChip or
CURRENT_COPY != rebootFile.mechanismNextCopy) {
std::string infoString = std::to_string(rebootFile.mechanismNextChip) + " " +
std::to_string(rebootFile.mechanismNextCopy);
if (rebootWatchdogFile.mechanismNextChip != xsc::NO_CHIP and
rebootWatchdogFile.mechanismNextCopy != xsc::NO_COPY) {
if (CURRENT_CHIP != rebootWatchdogFile.mechanismNextChip or
CURRENT_COPY != rebootWatchdogFile.mechanismNextCopy) {
std::string infoString = std::to_string(rebootWatchdogFile.mechanismNextChip) + " " +
std::to_string(rebootWatchdogFile.mechanismNextCopy);
sif::warning << "CoreController::performRebootFileHandling: Expected to be on image "
<< infoString << " but currently on other image. Locking " << infoString
<< std::endl;
// Firmware or other component might be corrupt and we are on another image then the target
// image specified by the mechanism. We can't really trust the target image anymore.
// Lock it for now
if (rebootFile.mechanismNextChip == xsc::CHIP_0) {
if (rebootFile.mechanismNextCopy == xsc::COPY_0) {
rebootFile.img00Lock = true;
if (rebootWatchdogFile.mechanismNextChip == xsc::CHIP_0) {
if (rebootWatchdogFile.mechanismNextCopy == xsc::COPY_0) {
rebootWatchdogFile.img00Lock = true;
} else {
rebootFile.img01Lock = true;
rebootWatchdogFile.img01Lock = true;
}
} else {
if (rebootFile.mechanismNextCopy == xsc::COPY_0) {
rebootFile.img10Lock = true;
if (rebootWatchdogFile.mechanismNextCopy == xsc::COPY_0) {
rebootWatchdogFile.img10Lock = true;
} else {
rebootFile.img11Lock = true;
rebootWatchdogFile.img11Lock = true;
}
}
}
}
rebootFile.lastChip = CURRENT_CHIP;
rebootFile.lastCopy = CURRENT_COPY;
rebootWatchdogFile.lastChip = CURRENT_CHIP;
rebootWatchdogFile.lastCopy = CURRENT_COPY;
// Only reboot if the reboot functionality is enabled.
// The handler will still increment the boot counts
if (rebootFile.enabled and (*rebootFile.relevantBootCnt >= rebootFile.maxCount)) {
if (rebootWatchdogFile.enabled and
(*rebootWatchdogFile.relevantBootCnt >= rebootWatchdogFile.maxCount)) {
// Reboot to other image
bool doReboot = false;
xsc::Chip tgtChip = xsc::NO_CHIP;
xsc::Copy tgtCopy = xsc::NO_COPY;
determineAndExecuteReboot(rebootFile, doReboot, tgtChip, tgtCopy);
rebootWatchdogAlgorithm(rebootWatchdogFile, doReboot, tgtChip, tgtCopy);
if (doReboot) {
rebootFile.bootFlag = true;
rebootWatchdogFile.bootFlag = true;
#if OBSW_VERBOSE_LEVEL >= 1
sif::info << "Boot counter for image " << CURRENT_CHIP << " " << CURRENT_COPY
<< " too high. Rebooting to " << tgtChip << " " << tgtCopy << std::endl;
#endif
rebootFile.mechanismNextChip = tgtChip;
rebootFile.mechanismNextCopy = tgtCopy;
rewriteRebootFile(rebootFile);
xsc_boot_copy(static_cast<xsc_libnor_chip_t>(tgtChip),
static_cast<xsc_libnor_copy_t>(tgtCopy));
rebootWatchdogFile.mechanismNextChip = tgtChip;
rebootWatchdogFile.mechanismNextCopy = tgtCopy;
rewriteRebootWatchdogFile(rebootWatchdogFile);
performGracefulShutdown(tgtChip, tgtCopy);
}
} else {
rebootFile.mechanismNextChip = xsc::NO_CHIP;
rebootFile.mechanismNextCopy = xsc::NO_COPY;
rebootWatchdogFile.mechanismNextChip = xsc::NO_CHIP;
rebootWatchdogFile.mechanismNextCopy = xsc::NO_COPY;
}
rewriteRebootFile(rebootFile);
rewriteRebootWatchdogFile(rebootWatchdogFile);
}
void CoreController::determineAndExecuteReboot(RebootFile &rf, bool &needsReboot,
void CoreController::rebootWatchdogAlgorithm(RebootWatchdogFile &rf, bool &needsReboot,
xsc::Chip &tgtChip, xsc::Copy &tgtCopy) {
tgtChip = xsc::CHIP_0;
tgtCopy = xsc::COPY_0;
@ -1826,7 +1806,7 @@ void CoreController::determineAndExecuteReboot(RebootFile &rf, bool &needsReboot
}
}
bool CoreController::parseRebootFile(std::string path, RebootFile &rf) {
bool CoreController::parseRebootWatchdogFile(std::string path, RebootWatchdogFile &rf) {
using namespace std;
std::string selfMatch;
if (CURRENT_CHIP == xsc::CHIP_0) {
@ -2008,34 +1988,123 @@ bool CoreController::parseRebootFile(std::string path, RebootFile &rf) {
return true;
}
void CoreController::resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy) {
std::string path = currMntPrefix + REBOOT_FILE;
parseRebootFile(path, rebootFile);
bool CoreController::parseRebootCountersFile(std::string path, RebootCountersFile &rf) {
using namespace std;
ifstream file(path);
string word;
string line;
uint8_t lineIdx = 0;
while (std::getline(file, line)) {
istringstream iss(line);
switch (lineIdx) {
case 0: {
iss >> word;
if (word.find("img00:") == string::npos) {
return false;
}
iss >> rf.img00Cnt;
break;
}
case 1: {
iss >> word;
if (word.find("img01:") == string::npos) {
return false;
}
iss >> rf.img01Cnt;
break;
}
case 2: {
iss >> word;
if (word.find("img10:") == string::npos) {
return false;
}
iss >> rf.img10Cnt;
break;
}
case 3: {
iss >> word;
if (word.find("img11:") == string::npos) {
return false;
}
iss >> rf.img11Cnt;
break;
}
}
lineIdx++;
}
return true;
}
void CoreController::resetRebootWatchdogCounters(xsc::Chip tgtChip, xsc::Copy tgtCopy) {
std::string path = currMntPrefix + REBOOT_WATCHDOG_FILE;
parseRebootWatchdogFile(path, rebootWatchdogFile);
if (tgtChip == xsc::ALL_CHIP and tgtCopy == xsc::ALL_COPY) {
rebootFile.img00Cnt = 0;
rebootFile.img01Cnt = 0;
rebootFile.img10Cnt = 0;
rebootFile.img11Cnt = 0;
rebootWatchdogFile.img00Cnt = 0;
rebootWatchdogFile.img01Cnt = 0;
rebootWatchdogFile.img10Cnt = 0;
rebootWatchdogFile.img11Cnt = 0;
} else {
if (tgtChip == xsc::CHIP_0) {
if (tgtCopy == xsc::COPY_0) {
rebootFile.img00Cnt = 0;
rebootWatchdogFile.img00Cnt = 0;
} else {
rebootFile.img01Cnt = 0;
rebootWatchdogFile.img01Cnt = 0;
}
} else {
if (tgtCopy == xsc::COPY_0) {
rebootFile.img10Cnt = 0;
rebootWatchdogFile.img10Cnt = 0;
} else {
rebootFile.img11Cnt = 0;
rebootWatchdogFile.img11Cnt = 0;
}
}
}
rewriteRebootFile(rebootFile);
rewriteRebootWatchdogFile(rebootWatchdogFile);
}
void CoreController::rewriteRebootFile(RebootFile file) {
std::string path = currMntPrefix + REBOOT_FILE;
void CoreController::performRebootCountersHandling(bool recreateFile) {
std::string path = currMntPrefix + REBOOT_COUNTERS_FILE;
std::error_code e;
if (not std::filesystem::exists(path, e) or recreateFile) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::info << "CoreController::performRebootFileHandling: Recreating reboot counters file"
<< std::endl;
#endif
rebootCountersFile.img00Cnt = 0;
rebootCountersFile.img01Cnt = 0;
rebootCountersFile.img10Cnt = 0;
rebootCountersFile.img11Cnt = 0;
rewriteRebootCountersFile(rebootCountersFile);
} else {
if (not parseRebootCountersFile(path, rebootCountersFile)) {
performRebootCountersHandling(true);
return;
}
}
if (CURRENT_CHIP == xsc::CHIP_0) {
if (CURRENT_COPY == xsc::COPY_0) {
rebootCountersFile.img00Cnt++;
} else {
rebootCountersFile.img01Cnt++;
}
} else {
if (CURRENT_COPY == xsc::COPY_0) {
rebootCountersFile.img10Cnt++;
} else {
rebootCountersFile.img11Cnt++;
}
}
announceBootCounts();
rewriteRebootCountersFile(rebootCountersFile);
}
void CoreController::rewriteRebootWatchdogFile(RebootWatchdogFile file) {
using namespace std::filesystem;
std::string path = currMntPrefix + REBOOT_WATCHDOG_FILE;
std::string legacyPath = currMntPrefix + LEGACY_REBOOT_WATCHDOG_FILE;
{
std::ofstream rebootFile(path);
if (rebootFile.is_open()) {
// Initiate reboot file first. Reboot handling will be on on initialization
@ -2049,26 +2118,44 @@ void CoreController::rewriteRebootFile(RebootFile file) {
<< "\nnext: " << static_cast<int>(file.mechanismNextChip) << " "
<< static_cast<int>(file.mechanismNextCopy) << "\n";
}
}
std::error_code e;
// TODO: Remove at some point in the future when all images have been updated.
if (std::filesystem::exists(legacyPath)) {
// Keep those two files in sync
std::filesystem::copy(path, legacyPath, std::filesystem::copy_options::overwrite_existing, e);
if (e) {
sif::error << "File copy has failed: " << e.message() << std::endl;
}
}
}
void CoreController::rewriteRebootCountersFile(RebootCountersFile file) {
std::string path = currMntPrefix + REBOOT_COUNTERS_FILE;
std::ofstream rebootFile(path);
if (rebootFile.is_open()) {
rebootFile << "img00: " << file.img00Cnt << "\nimg01: " << file.img01Cnt
<< "\nimg10: " << file.img10Cnt << "\nimg11: " << file.img11Cnt << "\n";
}
}
void CoreController::setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::Copy tgtCopy) {
std::string path = currMntPrefix + REBOOT_FILE;
// Disable the reboot file mechanism
parseRebootFile(path, rebootFile);
std::string path = currMntPrefix + REBOOT_WATCHDOG_FILE;
parseRebootWatchdogFile(path, rebootWatchdogFile);
if (tgtChip == xsc::CHIP_0) {
if (tgtCopy == xsc::COPY_0) {
rebootFile.img00Lock = lock;
rebootWatchdogFile.img00Lock = lock;
} else {
rebootFile.img01Lock = lock;
rebootWatchdogFile.img01Lock = lock;
}
} else {
if (tgtCopy == xsc::COPY_0) {
rebootFile.img10Lock = lock;
rebootWatchdogFile.img10Lock = lock;
} else {
rebootFile.img11Lock = lock;
rebootWatchdogFile.img11Lock = lock;
}
}
rewriteRebootFile(rebootFile);
rewriteRebootWatchdogFile(rebootWatchdogFile);
}
ReturnValue_t CoreController::backupTimeFileHandler() {
@ -2355,10 +2442,12 @@ bool CoreController::startSdStateMachine(sd::SdCard targetActiveSd, SdCfgMode mo
}
void CoreController::announceBootCounts() {
uint64_t totalBootCount =
rebootFile.img00Cnt + rebootFile.img01Cnt + rebootFile.img10Cnt + rebootFile.img11Cnt;
uint32_t individualBootCountsP1 = (rebootFile.img00Cnt << 16) | rebootFile.img01Cnt;
uint32_t individualBootCountsP2 = (rebootFile.img10Cnt << 16) | rebootFile.img11Cnt;
uint64_t totalBootCount = rebootCountersFile.img00Cnt + rebootCountersFile.img01Cnt +
rebootCountersFile.img10Cnt + rebootCountersFile.img11Cnt;
uint32_t individualBootCountsP1 =
(rebootCountersFile.img00Cnt << 16) | rebootCountersFile.img01Cnt;
uint32_t individualBootCountsP2 =
(rebootCountersFile.img10Cnt << 16) | rebootCountersFile.img11Cnt;
triggerEvent(core::INDIVIDUAL_BOOT_COUNTS, individualBootCountsP1, individualBootCountsP2);
triggerEvent(core::REBOOT_COUNTER, (totalBootCount >> 32) & 0xffffffff,
totalBootCount & 0xffffffff);
@ -2431,11 +2520,14 @@ void CoreController::announceVersionInfo() {
}
triggerEvent(VERSION_INFO, p1, p2);
if (common::OBSW_VERSION_MAJOR >= 6 or common::OBSW_VERSION_MAJOR == 4) {
if (mappedSysRomAddr != nullptr) {
uint32_t p1Firmware = *(reinterpret_cast<uint32_t *>(mappedSysRomAddr));
uint32_t p2Firmware = *(reinterpret_cast<uint32_t *>(mappedSysRomAddr) + 1);
triggerEvent(FIRMWARE_INFO, p1Firmware, p2Firmware);
}
}
}
void CoreController::announceCurrentImageInfo() {
@ -2443,6 +2535,50 @@ void CoreController::announceCurrentImageInfo() {
triggerEvent(CURRENT_IMAGE_INFO, CURRENT_CHIP, CURRENT_COPY);
}
ReturnValue_t CoreController::performGracefulShutdown(xsc::Chip tgtChip, xsc::Copy tgtCopy) {
bool protOpPerformed = false;
// This function can not really fail
gracefulShutdownTasks(tgtChip, tgtCopy, protOpPerformed);
switch (tgtChip) {
case (xsc::Chip::CHIP_0): {
switch (tgtCopy) {
case (xsc::Copy::COPY_0): {
xsc_boot_copy(XSC_LIBNOR_CHIP_0, XSC_LIBNOR_COPY_NOMINAL);
break;
}
case (xsc::Copy::COPY_1): {
xsc_boot_copy(XSC_LIBNOR_CHIP_0, XSC_LIBNOR_COPY_GOLD);
break;
}
default: {
break;
}
}
break;
}
case (xsc::Chip::CHIP_1): {
switch (tgtCopy) {
case (xsc::Copy::COPY_0): {
xsc_boot_copy(XSC_LIBNOR_CHIP_1, XSC_LIBNOR_COPY_NOMINAL);
break;
}
case (xsc::Copy::COPY_1): {
xsc_boot_copy(XSC_LIBNOR_CHIP_1, XSC_LIBNOR_COPY_GOLD);
break;
}
default: {
break;
}
}
break;
}
default:
break;
}
return returnvalue::OK;
}
bool CoreController::isNumber(const std::string &s) {
return !s.empty() && std::find_if(s.begin(), s.end(),
[](unsigned char c) { return !std::isdigit(c); }) == s.end();

View File

@ -24,7 +24,7 @@
class Timer;
class SdCardManager;
struct RebootFile {
struct RebootWatchdogFile {
static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10;
bool enabled = true;
@ -45,9 +45,9 @@ struct RebootFile {
xsc::Copy mechanismNextCopy = xsc::Copy::NO_COPY;
};
class RebootMechanismPacket : public SerialLinkedListAdapter<SerializeIF> {
class RebootWatchdogPacket : public SerialLinkedListAdapter<SerializeIF> {
public:
RebootMechanismPacket(RebootFile& rf) {
RebootWatchdogPacket(RebootWatchdogFile& rf) {
enabled = rf.enabled;
maxCount = rf.maxCount;
img00Count = rf.img00Cnt;
@ -100,6 +100,38 @@ class RebootMechanismPacket : public SerialLinkedListAdapter<SerializeIF> {
SerializeElement<uint8_t> nextCopy = 0;
};
struct RebootCountersFile {
// 16 bit values so all boot counters fit into one event.
uint16_t img00Cnt = 0;
uint16_t img01Cnt = 0;
uint16_t img10Cnt = 0;
uint16_t img11Cnt = 0;
};
class RebootCountersPacket : public SerialLinkedListAdapter<SerializeIF> {
RebootCountersPacket(RebootCountersFile& rf) {
img00Count = rf.img00Cnt;
img01Count = rf.img01Cnt;
img10Count = rf.img10Cnt;
img11Count = rf.img11Cnt;
setLinks();
}
private:
void setLinks() {
setStart(&img00Count);
img00Count.setNext(&img01Count);
img01Count.setNext(&img10Count);
img10Count.setNext(&img11Count);
setLast(&img11Count);
}
SerializeElement<uint16_t> img00Count = 0;
SerializeElement<uint16_t> img01Count = 0;
SerializeElement<uint16_t> img10Count = 0;
SerializeElement<uint16_t> img11Count = 0;
};
class CoreController : public ExtendedControllerBase, public ReceivesParameterMessagesIF {
public:
enum ParamId : uint8_t { PREF_SD = 0, NUM_IDS };
@ -113,10 +145,15 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe
const std::string VERSION_FILE =
"/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::VERSION_FILE_NAME);
const std::string REBOOT_FILE =
"/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::REBOOT_FILE_NAME);
const std::string LEGACY_REBOOT_WATCHDOG_FILE =
"/" + std::string(core::CONF_FOLDER) + "/" +
std::string(core::LEGACY_REBOOT_WATCHDOG_FILE_NAME);
const std::string REBOOT_WATCHDOG_FILE =
"/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::REBOOT_WATCHDOG_FILE_NAME);
const std::string BACKUP_TIME_FILE =
"/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::TIME_FILE_NAME);
const std::string REBOOT_COUNTERS_FILE =
"/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::REBOOT_COUNTER_FILE_NAME);
static constexpr char CHIP_0_COPY_0_MOUNT_DIR[] = "/tmp/mntupdate-xdi-qspi0-nom-rootfs";
static constexpr char CHIP_0_COPY_1_MOUNT_DIR[] = "/tmp/mntupdate-xdi-qspi0-gold-rootfs";
@ -248,7 +285,8 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe
std::array<uint8_t, 1024> dirListingBuf{};
DirListingDumpContext dumpContext{};
RebootFile rebootFile = {};
RebootWatchdogFile rebootWatchdogFile = {};
RebootCountersFile rebootCountersFile = {};
CommandExecutor cmdExecutor;
SimpleRingBuffer cmdReplyBuf;
@ -318,12 +356,14 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe
void currentStateSetter(sd::SdCard sdCard, sd::SdState newState);
void executeNextExternalSdCommand();
void checkExternalSdCommandStatus();
void performRebootFileHandling(bool recreateFile);
void performRebootWatchdogHandling(bool recreateFile);
void performRebootCountersHandling(bool recreateFile);
ReturnValue_t actionListDirectoryIntoFile(ActionId_t actionId, MessageQueueId_t commandedBy,
const uint8_t* data, size_t size);
ReturnValue_t actionListDirectoryDumpDirectly(ActionId_t actionId, MessageQueueId_t commandedBy,
const uint8_t* data, size_t size);
ReturnValue_t performGracefulShutdown(xsc::Chip targetChip, xsc::Copy targetCopy);
ReturnValue_t actionListDirectoryCommonCommandCreator(const uint8_t* data, size_t size,
std::ostringstream& oss);
@ -337,12 +377,14 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe
int handleBootCopyProtAtIndex(xsc::Chip targetChip, xsc::Copy targetCopy, bool protect,
bool& protOperationPerformed, bool selfChip, bool selfCopy,
bool allChips, bool allCopies, uint8_t arrIdx);
void determineAndExecuteReboot(RebootFile& rf, bool& needsReboot, xsc::Chip& tgtChip,
void rebootWatchdogAlgorithm(RebootWatchdogFile& rf, bool& needsReboot, xsc::Chip& tgtChip,
xsc::Copy& tgtCopy);
void resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy);
void resetRebootWatchdogCounters(xsc::Chip tgtChip, xsc::Copy tgtCopy);
void setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::Copy tgtCopy);
bool parseRebootFile(std::string path, RebootFile& file);
void rewriteRebootFile(RebootFile file);
bool parseRebootWatchdogFile(std::string path, RebootWatchdogFile& file);
bool parseRebootCountersFile(std::string path, RebootCountersFile& file);
void rewriteRebootWatchdogFile(RebootWatchdogFile file);
void rewriteRebootCountersFile(RebootCountersFile file);
void announceBootCounts();
void announceVersionInfo();
void announceCurrentImageInfo();

View File

@ -731,12 +731,20 @@ ReturnValue_t ObjectFactory::createCcsdsComponents(CcsdsComponentArgs& args) {
// GPIO definitions of signals connected to the virtual channel interfaces of the PTME IP Core
GpioCookie* gpioCookiePtmeIp = new GpioCookie;
GpiodRegularByLineName* gpio = nullptr;
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC0, "PAPB VC0");
gpioCookiePtmeIp->addGpio(gpioIds::VC0_PAPB_BUSY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC0, "PAPB VC0");
gpioCookiePtmeIp->addGpio(gpioIds::VC0_PAPB_EMPTY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC1, "PAPB VC1");
gpioCookiePtmeIp->addGpio(gpioIds::VC1_PAPB_BUSY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC1, "PAPB VC1");
gpioCookiePtmeIp->addGpio(gpioIds::VC1_PAPB_EMPTY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC2, "PAPB VC2");
gpioCookiePtmeIp->addGpio(gpioIds::VC2_PAPB_BUSY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC2, "PAPB VC2");
gpioCookiePtmeIp->addGpio(gpioIds::VC2_PAPB_EMPTY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC3, "PAPB VC3");
gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_BUSY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC3, "PAPB VC3");
gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_EMPTY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PTME_RESETN, "PTME RESETN",
@ -745,14 +753,19 @@ ReturnValue_t ObjectFactory::createCcsdsComponents(CcsdsComponentArgs& args) {
gpioChecker(args.gpioComIF.addGpios(gpioCookiePtmeIp), "PTME PAPB VCs");
// Creating virtual channel interfaces
VirtualChannelIF* vc0 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC0_PAPB_EMPTY,
VirtualChannelIF* vc0 =
new PapbVcInterface(&args.gpioComIF, gpioIds::VC0_PAPB_BUSY, gpioIds::VC0_PAPB_EMPTY,
q7s::UIO_PTME, q7s::uiomapids::PTME_VC0);
VirtualChannelIF* vc1 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC1_PAPB_EMPTY,
VirtualChannelIF* vc1 =
new PapbVcInterface(&args.gpioComIF, gpioIds::VC1_PAPB_BUSY, gpioIds::VC1_PAPB_EMPTY,
q7s::UIO_PTME, q7s::uiomapids::PTME_VC1);
VirtualChannelIF* vc2 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC2_PAPB_EMPTY,
VirtualChannelIF* vc2 =
new PapbVcInterface(&args.gpioComIF, gpioIds::VC2_PAPB_BUSY, gpioIds::VC2_PAPB_EMPTY,
q7s::UIO_PTME, q7s::uiomapids::PTME_VC2);
VirtualChannelIF* vc3 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC3_PAPB_EMPTY,
VirtualChannelIF* vc3 =
new PapbVcInterface(&args.gpioComIF, gpioIds::VC3_PAPB_BUSY, gpioIds::VC3_PAPB_EMPTY,
q7s::UIO_PTME, q7s::uiomapids::PTME_VC3);
// Creating ptme object and adding virtual channel interfaces
Ptme* ptme = new Ptme(objects::PTME);
ptme->addVcInterface(ccsds::VC0, vc0);
@ -1017,6 +1030,7 @@ void ObjectFactory::createRadSensorChipSelect(LinuxLibgpioIF* gpioIF) {
void ObjectFactory::createPlI2cResetGpio(LinuxLibgpioIF* gpioIF) {
using namespace gpio;
if (common::OBSW_VERSION_MAJOR >= 6 or common::OBSW_VERSION_MAJOR == 4) {
if (gpioIF == nullptr) {
return;
}
@ -1029,4 +1043,5 @@ void ObjectFactory::createPlI2cResetGpio(LinuxLibgpioIF* gpioIF) {
gpioIF->pullLow(gpioIds::PL_I2C_ARESETN);
TaskFactory::delayTask(1);
gpioIF->pullHigh(gpioIds::PL_I2C_ARESETN);
}
}

View File

@ -324,6 +324,10 @@ void scheduling::initTasks() {
if (result != returnvalue::OK) {
scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER);
}
result = tcsSystemTask->addComponent(objects::HEATER_HANDLER);
if (result != returnvalue::OK) {
scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER);
}
#if OBSW_ADD_SYRLINKS == 1
PeriodicTaskIF* syrlinksCom = factory->createPeriodicTask(

View File

@ -36,8 +36,8 @@ void ObjectFactory::produce(void* args) {
PersistentTmStores stores;
ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel,
*SdCardManager::instance(), &ipcStore, &tmStore, stores,
200);
*SdCardManager::instance(), &ipcStore, &tmStore, stores, 200,
enableHkSets);
LinuxLibgpioIF* gpioComIF = nullptr;
SerialComIF* uartComIF = nullptr;

View File

@ -33,8 +33,8 @@ void ObjectFactory::produce(void* args) {
PersistentTmStores stores;
ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel,
*SdCardManager::instance(), &ipcStore, &tmStore, stores,
200);
*SdCardManager::instance(), &ipcStore, &tmStore, stores, 200,
true);
LinuxLibgpioIF* gpioComIF = nullptr;
SerialComIF* uartComIF = nullptr;

View File

@ -96,9 +96,13 @@ enum gpioId_t {
SPI_MUX,
VC0_PAPB_EMPTY,
VC0_PAPB_BUSY,
VC1_PAPB_EMPTY,
VC1_PAPB_BUSY,
VC2_PAPB_EMPTY,
VC2_PAPB_BUSY,
VC3_PAPB_EMPTY,
VC3_PAPB_BUSY,
PTME_RESETN,
PDEC_RESET,

View File

@ -96,6 +96,25 @@ ReturnValue_t TemperatureSensorInserter::performOperation(uint8_t opCode) {
}
break;
}
case (TestCase::COLD_PLOC_CONSECUTIVE): {
if (cycles == 15) {
sif::debug << "Setting cold PLOC temperature" << std::endl;
max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(-15, true);
}
if (cycles == 30) {
sif::debug << "Setting warmer PLOC temperature" << std::endl;
max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(0, true);
}
if (cycles == 45) {
sif::debug << "Setting cold PLOC temperature again" << std::endl;
max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(-15, true);
}
if (cycles == 60) {
sif::debug << "Setting warmer PLOC temperature again" << std::endl;
max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(0, true);
}
break;
}
case (TestCase::COLD_CAMERA): {
if (cycles == 15) {
sif::debug << "Setting cold CAM temperature" << std::endl;

View File

@ -32,6 +32,7 @@ class TemperatureSensorInserter : public ExecutableObjectIF, public SystemObject
COLD_STR = 4,
COLD_STR_CONSECUTIVE = 5,
COLD_CAMERA = 6,
COLD_PLOC_CONSECUTIVE = 7,
};
int iteration = 0;
uint32_t cycles = 0;

2
fsfw

@ -1 +1 @@
Subproject commit 0f76cdb3ba54f5e90a8eee4316c49cf0f581f996
Subproject commit 6aff3250c29f5243eb4a6111ba0a5c0cc1a3033c

View File

@ -7,16 +7,20 @@
#include "fsfw/serviceinterface/ServiceInterface.h"
PapbVcInterface::PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbEmptyId,
std::string uioFile, int mapNum)
: gpioComIF(gpioComIF), papbEmptyId(papbEmptyId), uioFile(std::move(uioFile)), mapNum(mapNum) {}
PapbVcInterface::PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbBusyId,
gpioId_t papbEmptyId, std::string uioFile, int mapNum)
: gpioComIF(gpioComIF),
papbBusyId(papbBusyId),
papbEmptyId(papbEmptyId),
uioFile(std::move(uioFile)),
mapNum(mapNum) {}
PapbVcInterface::~PapbVcInterface() {}
ReturnValue_t PapbVcInterface::initialize() {
UioMapper uioMapper(uioFile, mapNum);
ReturnValue_t result = uioMapper.getMappedAdress(const_cast<uint32_t**>(&vcBaseReg),
UioMapper::Permissions::READ_WRITE);
UioMapper::Permissions::WRITE_ONLY);
if (result != returnvalue::OK) {
return result;
}
@ -28,16 +32,63 @@ ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size) {
if (size < 4) {
return returnvalue::FAILED;
}
if (pollReadyForPacket()) {
if (pollInterfaceReadiness(0, true) == returnvalue::OK) {
startPacketTransfer(ByteWidthCfg::ONE);
} else {
return DirectTmSinkIF::IS_BUSY;
}
// TODO: This should work but does not.. :(
// size_t idx = 0;
// while (idx < size) {
//
// nanosleep(&BETWEEN_POLL_DELAY, &remDelay);
// if ((size - idx) < 4) {
// *vcBaseReg = CONFIG_DATA_INPUT | (size - idx - 1);
// usleep(1);
// }
// if (pollPapbBusySignal(2) == returnvalue::OK) {
// // vcBaseReg + DATA_REG_OFFSET + 3 = static_cast<uint8_t>(data + idx);
// // vcBaseReg + DATA_REG_OFFSET + 2 = static_cast<uint8_t>(data + idx + 1);
// // vcBaseReg + DATA_REG_OFFSET + 1 = static_cast<uint8_t>(data + idx + 2);
// // vcBaseReg + DATA_REG_OFFSET = static_cast<uint8_t>(data + idx + 3);
//
// // std::memcpy((vcBaseReg + DATA_REG_OFFSET), data + idx , nextWriteSize);
// *(vcBaseReg + DATA_REG_OFFSET) = *reinterpret_cast<const uint32_t*>(data + idx);
// //uint8_t* byteReg = reinterpret_cast<uint8_t*>(vcBaseReg + DATA_REG_OFFSET);
//
// //byteReg[0] = data[idx];
// //byteReg[1] = data[idx];
// } else {
// abortPacketTransfer();
// return returnvalue::FAILED;
// }
// // TODO: Change this after the bugfix. Right now, the PAPB ignores the content of the byte
// // width configuration.5
// // It's okay to increment by a larger amount for the last segment here, loop will be over
// // in any case.
// idx += 4;
// }
for (size_t idx = 0; idx < size; idx++) {
// if (pollInterfaceReadiness(2, false) == returnvalue::OK) {
// This delay is super-important, DO NOT REMOVE!
// Polling the GPIO or the config register too often messes up the scheduler.
// TODO: Maybe this should not be done like this. It would be better if there was a custom
// FPGA module which can accept packets and then takes care of dumping that packet into
// the PTME. DMA would be an ideal solution for this.
nanosleep(&BETWEEN_POLL_DELAY, &remDelay);
if (pollInterfaceReadiness(2, false) == returnvalue::OK) {
*(vcBaseReg + DATA_REG_OFFSET) = static_cast<uint32_t>(data[idx]);
} else {
abortPacketTransfer();
return returnvalue::FAILED;
}
}
nanosleep(&BETWEEN_POLL_DELAY, &remDelay);
if (pollInterfaceReadiness(2, false) == returnvalue::OK) {
completePacketTransfer();
} else {
abortPacketTransfer();
return returnvalue::FAILED;
}
return returnvalue::OK;
}
@ -47,33 +98,60 @@ void PapbVcInterface::startPacketTransfer(ByteWidthCfg initWidth) {
void PapbVcInterface::completePacketTransfer() { *vcBaseReg = CONFIG_END; }
bool PapbVcInterface::pollReadyForPacket() const {
ReturnValue_t PapbVcInterface::pollInterfaceReadiness(uint32_t maxPollRetries,
bool checkReadyState) const {
uint32_t busyIdx = 0;
nextDelay.tv_nsec = FIRST_DELAY_PAPB_POLLING_NS;
while (true) {
// Check if PAPB interface is ready to receive data. Use the configuration register for this.
// Bit 5, see PTME ptme_001_01-0-7-r2 Table 31.
uint32_t reg = *vcBaseReg;
// bool busy = (reg >> 5) & 0b1;
return (reg >> 6) & 0b1;
bool busy = (reg >> 5) & 0b1;
bool ready = (reg >> 6) & 0b1;
if (not busy) {
return returnvalue::OK;
}
if (checkReadyState and not ready) {
return PAPB_BUSY;
}
busyIdx++;
if (busyIdx >= maxPollRetries) {
return PAPB_BUSY;
}
// Ignore signal handling here for now.
nanosleep(&nextDelay, &remDelay);
// Adaptive delay.
if (nextDelay.tv_nsec * 2 <= MAX_DELAY_PAPB_POLLING_NS) {
nextDelay.tv_nsec *= 2;
}
}
return returnvalue::OK;
}
bool PapbVcInterface::isVcInterfaceBufferEmpty() {
void PapbVcInterface::isVcInterfaceBufferEmpty() {
ReturnValue_t result = returnvalue::OK;
gpio::Levels papbEmptyState = gpio::Levels::HIGH;
result = gpioComIF->readGpio(papbEmptyId, papbEmptyState);
if (result != returnvalue::OK) {
sif::error << "PapbVcInterface::isVcInterfaceBufferEmpty: Failed to read papb empty signal"
sif::warning << "PapbVcInterface::isVcInterfaceBufferEmpty: Failed to read papb empty signal"
<< std::endl;
return true;
return;
}
if (papbEmptyState == gpio::Levels::HIGH) {
return true;
sif::debug << "PapbVcInterface::isVcInterfaceBufferEmpty: Buffer is empty" << std::endl;
} else {
sif::debug << "PapbVcInterface::isVcInterfaceBufferEmpty: Buffer is not empty" << std::endl;
}
return false;
return;
}
bool PapbVcInterface::isBusy() const { return not pollReadyForPacket(); }
bool PapbVcInterface::isBusy() const { return pollInterfaceReadiness(0, true) == PAPB_BUSY; }
void PapbVcInterface::cancelTransfer() { abortPacketTransfer(); }

View File

@ -30,7 +30,8 @@ class PapbVcInterface : public VirtualChannelIF {
* @param uioFile UIO file providing access to the PAPB bus
* @param mapNum Map number of UIO map associated with this virtual channel
*/
PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbEmptyId, std::string uioFile, int mapNum);
PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbBusyId, gpioId_t papbEmptyId,
std::string uioFile, int mapNum);
virtual ~PapbVcInterface();
bool isBusy() const override;
@ -82,6 +83,9 @@ class PapbVcInterface : public VirtualChannelIF {
static constexpr long int MAX_DELAY_PAPB_POLLING_NS = 40;
LinuxLibgpioIF* gpioComIF = nullptr;
/** Pulled to low when virtual channel not ready to receive data */
gpioId_t papbBusyId = gpio::NO_GPIO;
/** High when external buffer memory of virtual channel is empty */
gpioId_t papbEmptyId = gpio::NO_GPIO;
@ -116,13 +120,13 @@ class PapbVcInterface : public VirtualChannelIF {
*
* @return returnvalue::OK when ready to receive data else PAPB_BUSY.
*/
inline bool pollReadyForPacket() const;
inline ReturnValue_t pollInterfaceReadiness(uint32_t maxPollRetries, bool checkReadyState) const;
/**
* @brief This function can be used for debugging to check whether there are packets in
* the packet buffer of the virtual channel or not.
*/
bool isVcInterfaceBufferEmpty();
void isVcInterfaceBufferEmpty();
/**
* @brief This function sends a complete telemetry transfer frame data field (1105 bytes)

View File

@ -42,7 +42,13 @@ ReturnValue_t PersistentLogTmStoreTask::performOperation(uint8_t opCode) {
if (not someonesBusy) {
TaskFactory::delayTask(100);
} else if (vcBusyDuringDump) {
// TODO: Might not be necessary
sif::debug << "VC busy, delaying" << std::endl;
TaskFactory::delayTask(10);
} else {
// TODO: Would be best to remove this, but not delaying here can lead to evil issues.
// Polling the PAPB of the PTME core too often leads to scheuduling issues.
TaskFactory::delayTask(2);
}
}
}

View File

@ -24,7 +24,13 @@ ReturnValue_t PersistentSingleTmStoreTask::performOperation(uint8_t opCode) {
if (not busy) {
TaskFactory::delayTask(100);
} else if (dumpContext.vcBusyDuringDump) {
sif::debug << "VC busy, delaying" << std::endl;
// TODO: Might not be necessary
TaskFactory::delayTask(10);
} else {
// TODO: Would be best to remove this, but not delaying here can lead to evil issues.
// Polling the PAPB of the PTME core too often leads to scheuduling issues.
TaskFactory::delayTask(2);
}
}
}

View File

@ -96,14 +96,14 @@ void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFun
CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan,
StorageManagerIF** ipcStore, StorageManagerIF** tmStore,
PersistentTmStores& stores,
uint32_t eventManagerQueueDepth) {
uint32_t eventManagerQueueDepth, bool enableHkSets) {
// Framework objects
new EventManager(objects::EVENT_MANAGER, eventManagerQueueDepth);
auto healthTable = new HealthTable(objects::HEALTH_TABLE);
if (healthTable_ != nullptr) {
*healthTable_ = healthTable;
}
new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER);
new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER, 5, enableHkSets, 120);
new VerificationReporter();
auto* timeStamper = new CdsShortTimeStamper(objects::TIME_STAMPER);
StorageManagerIF* tcStore;

View File

@ -45,7 +45,8 @@ namespace ObjectFactory {
void produceGenericObjects(HealthTableIF** healthTable, PusTmFunnel** pusFunnel,
CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan,
StorageManagerIF** ipcStore, StorageManagerIF** tmStore,
PersistentTmStores& stores, uint32_t eventManagerQueueDepth);
PersistentTmStores& stores, uint32_t eventManagerQueueDepth,
bool enableHkSets);
void createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF& pwrSwitcher,
HeaterHandler*& heaterHandler);

View File

@ -41,7 +41,9 @@ enum SystemctlCmd : uint8_t { START = 0, STOP = 1, RESTART = 2, NUM_CMDS = 3 };
static constexpr char CONF_FOLDER[] = "conf";
static constexpr char VERSION_FILE_NAME[] = "version.txt";
static constexpr char REBOOT_FILE_NAME[] = "reboot.txt";
static constexpr char LEGACY_REBOOT_WATCHDOG_FILE_NAME[] = "reboot.txt";
static constexpr char REBOOT_WATCHDOG_FILE_NAME[] = "reboot_watchdog.txt";
static constexpr char REBOOT_COUNTER_FILE_NAME[] = "reboot_counters.txt";
static constexpr char TIME_FILE_NAME[] = "time_backup.txt";
static constexpr uint32_t SYS_ROM_BASE_ADDR = 0x80000000;

View File

@ -51,9 +51,13 @@ ReturnValue_t HeaterHandler::performOperation(uint8_t operationCode) {
if (mainLineSwitcher->getSwitchState(mainLineSwitch) == SWITCH_OFF) {
waitForSwitchOff = false;
mode = MODE_OFF;
busyWithSwitchCommanding = false;
modeHelper.modeChanged(mode, submode);
}
}
if (busyWithSwitchCommanding and heaterCmdBusyCd.hasTimedOut()) {
busyWithSwitchCommanding = false;
}
} catch (const std::out_of_range& e) {
sif::warning << "HeaterHandler::performOperation: "
"Out of range error | "
@ -101,23 +105,23 @@ ReturnValue_t HeaterHandler::initializeHeaterMap() {
void HeaterHandler::readCommandQueue() {
ReturnValue_t result = returnvalue::OK;
CommandMessage command;
do {
if (not busyWithSwitchCommanding) {
result = commandQueue->receiveMessage(&command);
if (result == MessageQueueIF::EMPTY) {
break;
return;
} else if (result != returnvalue::OK) {
sif::warning << "HeaterHandler::readCommandQueue: Message reception error" << std::endl;
break;
}
result = actionHelper.handleActionMessage(&command);
if (result == returnvalue::OK) {
continue;
return;
}
result = modeHelper.handleModeCommand(&command);
if (result == returnvalue::OK) {
continue;
return;
}
result = actionHelper.handleActionMessage(&command);
if (result == returnvalue::OK) {
return;
}
}
} while (result == returnvalue::OK);
}
ReturnValue_t HeaterHandler::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
@ -167,6 +171,8 @@ ReturnValue_t HeaterHandler::executeAction(ActionId_t actionId, MessageQueueId_t
heater.action = action;
heater.cmdActive = true;
heater.replyQueue = commandedBy;
busyWithSwitchCommanding = true;
heaterCmdBusyCd.resetTimer();
return returnvalue::OK;
}
@ -249,6 +255,7 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) {
sif::error << "HeaterHandler::handleSwitchOnCommand: Main switch setting on timeout"
<< std::endl;
heater.cmdActive = false;
busyWithSwitchCommanding = false;
heater.waitMainSwitchOn = false;
if (heater.replyQueue != commandQueue->getId()) {
actionHelper.finish(false, heater.replyQueue, heater.action, MAIN_SWITCH_SET_TIMEOUT);
@ -259,27 +266,25 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) {
// Check state of main line switch
ReturnValue_t mainSwitchState = mainLineSwitcher->getSwitchState(mainLineSwitch);
if (mainSwitchState == PowerSwitchIF::SWITCH_ON) {
if (getSwitchState(heaterIdx) == SwitchState::OFF) {
gpioId_t gpioId = heater.gpioId;
result = gpioInterface->pullHigh(gpioId);
if (result != returnvalue::OK) {
sif::error << "HeaterHandler::handleSwitchOnCommand: Failed to pull gpio with id " << gpioId
sif::error << "HeaterHandler::handleSwitchOnCommand: Failed to pull GPIO with ID " << gpioId
<< " high" << std::endl;
triggerEvent(GPIO_PULL_HIGH_FAILED, result);
} else {
}
if (result == returnvalue::OK) {
triggerEvent(HEATER_WENT_ON, heaterIdx, 0);
EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO,
MODE_ON, 0);
{
MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
heater.switchState = ON;
}
}
} else {
triggerEvent(SWITCH_ALREADY_ON, heaterIdx);
}
EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO,
MODE_ON, 0);
busyWithSwitchCommanding = false;
mode = HasModesIF::MODE_ON;
modeHelper.modeChanged(mode, submode);
}
// There is no need to send action finish replies if the sender was the
// HeaterHandler itself
if (heater.replyQueue != commandQueue->getId()) {
@ -312,30 +317,33 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) {
void HeaterHandler::handleSwitchOffCommand(heater::Switch heaterIdx) {
ReturnValue_t result = returnvalue::OK;
auto& heater = heaterVec.at(heaterIdx);
// Check whether switch is already off
if (getSwitchState(heaterIdx)) {
gpioId_t gpioId = heater.gpioId;
result = gpioInterface->pullLow(gpioId);
if (result != returnvalue::OK) {
sif::error << "HeaterHandler::handleSwitchOffCommand: Failed to pull gpio with id" << gpioId
<< " low" << std::endl;
triggerEvent(GPIO_PULL_LOW_FAILED, result);
} else {
}
if (result == returnvalue::OK) {
// Check whether switch is already off
if (getSwitchState(heaterIdx) == SwitchState::ON) {
{
MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
heater.switchState = OFF;
}
triggerEvent(HEATER_WENT_OFF, heaterIdx, 0);
} else {
triggerEvent(SWITCH_ALREADY_OFF, heaterIdx);
}
EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO,
MODE_OFF, 0);
// When all switches are off, also main line switch will be turned off
if (allSwitchesOff()) {
mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF);
waitForSwitchOff = true;
}
}
} else {
triggerEvent(SWITCH_ALREADY_OFF, heaterIdx);
busyWithSwitchCommanding = false;
}
}
if (heater.replyQueue != NO_COMMANDER) {
// Report back switch command reply if necessary

View File

@ -148,6 +148,7 @@ class HeaterHandler : public ExecutableObjectIF,
/** Size of command queue */
size_t cmdQueueSize = 20;
bool waitForSwitchOff = true;
bool busyWithSwitchCommanding = false;
GpioIF* gpioInterface = nullptr;
@ -163,6 +164,7 @@ class HeaterHandler : public ExecutableObjectIF,
power::Switch_t mainLineSwitch;
ActionHelper actionHelper;
Countdown heaterCmdBusyCd = Countdown(2000);
StorageManagerIF* ipcStore = nullptr;

View File

@ -17,6 +17,8 @@
using namespace returnvalue;
static constexpr bool DEBUG_DUMPS = false;
PersistentTmStore::PersistentTmStore(PersistentTmStoreArgs args)
: SystemObject(args.objectId),
tmStore(args.tmStore),
@ -32,6 +34,91 @@ ReturnValue_t PersistentTmStore::cancelDump() {
return returnvalue::OK;
}
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;
}
dumpParams.fileSize = std::filesystem::file_size(fileOrDir.path(), e);
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());
std::filesystem::remove(fileOrDir.path(), e);
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;
}
if (DEBUG_DUMPS) {
sif::debug << "Inserting file " << fileOrDir.path() << std::endl;
}
DumpIndex dumpIndex;
dumpIndex.epoch = fileEpoch;
// Multiple files for the same time are supported via a special suffix. We simply count the
// number of copies and later try to dump the same number of files with the additional
// suffixes
auto iter = dumpParams.orderedDumpFilestamps.find(dumpIndex);
if (iter != dumpParams.orderedDumpFilestamps.end()) {
dumpIndex.epoch = iter->epoch;
dumpIndex.additionalFiles = iter->additionalFiles + 1;
dumpParams.orderedDumpFilestamps.erase(dumpIndex);
} else {
dumpIndex.additionalFiles = 0;
}
dumpParams.orderedDumpFilestamps.emplace(dumpIndex);
}
}
return returnvalue::OK;
}
std::optional<uint8_t> PersistentTmStore::extractSuffix(const std::string& pathStr) {
std::string numberStr;
// Find the position of the dot at the end of the file path
size_t dotPos = pathStr.find_last_of('.');
if ((dotPos < pathStr.length()) and not std::isdigit(pathStr[dotPos + 1])) {
return std::nullopt;
}
// Extract the substring after the dot
numberStr = pathStr.substr(dotPos + 1);
std::optional<uint8_t> number;
try {
number = std::stoi(numberStr);
if (number.value() > std::numeric_limits<uint8_t>::max()) {
return std::nullopt;
}
} catch (std::invalid_argument& exception) {
sif::error << "PersistentTmStore::extractSuffix: Exception " << exception.what()
<< ", invald input string: " << numberStr << std::endl;
}
return number;
}
ReturnValue_t PersistentTmStore::assignAndOrCreateMostRecentFile() {
if (not activeFile.has_value()) {
return createMostRecentFile(std::nullopt);
@ -159,6 +246,12 @@ bool PersistentTmStore::updateBaseDir() {
if (not exists(basePath, e)) {
create_directories(basePath);
}
// 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);
filePathBuf[basePathSize] = '_';
basePathSize += 1;
baseDirUninitialized = false;
return true;
}
@ -189,12 +282,20 @@ ReturnValue_t PersistentTmStore::startDumpFromUpTo(uint32_t fromUnixSeconds,
if (state == State::DUMPING) {
return returnvalue::FAILED;
}
dumpParams.dirIter = directory_iterator(basePath);
if (dumpParams.dirIter == directory_iterator()) {
auto dirIter = directory_iterator(basePath);
// Directory empty case.
if (dirIter == directory_iterator()) {
return returnvalue::FAILED;
}
dumpParams.fromUnixTime = fromUnixSeconds;
dumpParams.untilUnixTime = upToUnixSeconds;
buildDumpSet(fromUnixSeconds, upToUnixSeconds);
// No files in time window found.
if (dumpParams.orderedDumpFilestamps.empty()) {
return DUMP_DONE;
}
dumpParams.dumpIter = dumpParams.orderedDumpFilestamps.begin();
dumpParams.currentSameFileIdx = std::nullopt;
state = State::DUMPING;
return loadNextDumpFile();
}
@ -203,50 +304,55 @@ ReturnValue_t PersistentTmStore::loadNextDumpFile() {
using namespace std::filesystem;
dumpParams.currentSize = 0;
std::error_code e;
for (; dumpParams.dirIter != directory_iterator(); dumpParams.dirIter++) {
dumpParams.dirEntry = *dumpParams.dirIter;
if (dumpParams.dirEntry.is_directory(e)) {
continue;
// Handle iteration, which does not necessarily have to increment the set iterator
// if there are multiple files for a given timestamp.
auto handleIteration = [&](DumpIndex& dumpIndex) {
if (dumpIndex.additionalFiles > 0) {
if (not dumpParams.currentSameFileIdx.has_value()) {
// Initialize the file index and stay on same file
dumpParams.currentSameFileIdx = 0;
return;
} else if (dumpParams.currentSameFileIdx.value() < dumpIndex.additionalFiles - 1) {
dumpParams.currentSameFileIdx = dumpParams.currentSameFileIdx.value() + 1;
return;
}
dumpParams.fileSize = std::filesystem::file_size(dumpParams.dirEntry.path(), e);
}
// File will change, reset this field for correct state-keeping.
dumpParams.currentSameFileIdx = std::nullopt;
// Increment iterator for next cycle.
dumpParams.dumpIter++;
};
while (dumpParams.dumpIter != dumpParams.orderedDumpFilestamps.end()) {
DumpIndex dumpIndex = *dumpParams.dumpIter;
timeval tv{};
tv.tv_sec = dumpIndex.epoch;
size_t fullPathLength = 0;
createFileName(tv, dumpParams.currentSameFileIdx, fullPathLength);
dumpParams.currentFile =
path(std::string(reinterpret_cast<const char*>(filePathBuf.data()), fullPathLength));
if (DEBUG_DUMPS) {
sif::debug << baseName << " dump: Loading " << dumpParams.currentFile << std::endl;
}
dumpParams.fileSize = std::filesystem::file_size(dumpParams.currentFile, e);
if (e) {
sif::error << "PersistentTmStore: Could not retrieve file size: " << e.message() << std::endl;
// TODO: Event?
sif::error << "PersistentTmStore: Could not load next dump file: " << e.message()
<< std::endl;
handleIteration(dumpIndex);
continue;
}
// sif::debug << "Path: " << dumpParams.dirEntry.path() << std::endl;
// 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());
std::filesystem::remove(dumpParams.dirEntry.path(), e);
continue;
}
const path& file = dumpParams.dirEntry.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)) {
dumpParams.currentFileUnixStamp = fileEpoch;
std::ifstream ifile(file, std::ios::binary);
std::ifstream ifile(dumpParams.currentFile, std::ios::binary);
if (ifile.bad()) {
sif::error << "PersistentTmStore: File is bad" << std::endl;
sif::error << "PersistentTmStore: File is bad. Loading next file" << std::endl;
handleIteration(dumpIndex);
continue;
}
ifile.read(reinterpret_cast<char*>(fileBuf.data()),
static_cast<std::streamsize>(dumpParams.fileSize));
// Increment iterator for next cycle.
dumpParams.dirIter++;
// Next file is loaded, exit the loop.
handleIteration(dumpIndex);
return returnvalue::OK;
}
}
// Directory iterator was consumed and we are done.
state = State::IDLE;
return DUMP_DONE;
@ -267,8 +373,8 @@ ReturnValue_t PersistentTmStore::getNextDumpPacket(PusTmReader& reader, bool& fi
// Delete the file and load next. Could use better algorithm to partially
// restore the file dump, but for now do not trust the file.
std::error_code e;
std::filesystem::remove(dumpParams.dirEntry.path().c_str(), e);
if (dumpParams.dirEntry.path() == activeFile) {
std::filesystem::remove(dumpParams.currentFile.c_str(), e);
if (dumpParams.currentFile == activeFile) {
activeFile == std::nullopt;
assignAndOrCreateMostRecentFile();
}
@ -302,37 +408,9 @@ ReturnValue_t PersistentTmStore::pathToTime(const std::filesystem::path& path, s
ReturnValue_t PersistentTmStore::createMostRecentFile(std::optional<uint8_t> suffix) {
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<char*>(fileBuf.data() + currentIdx),
fileBuf.size(), config::FILE_DATE_FORMAT, time);
if (writtenBytes == 0) {
sif::error << "PersistentTmStore::createMostRecentFile: Could not create file timestamp"
<< std::endl;
return returnvalue::FAILED;
}
currentIdx += writtenBytes;
char* res = strcpy(reinterpret_cast<char*>(fileBuf.data() + currentIdx), ".bin");
if (res == nullptr) {
return returnvalue::FAILED;
}
currentIdx += 4;
if (suffix.has_value()) {
std::string fullSuffix = "." + std::to_string(suffix.value());
res = strcpy(reinterpret_cast<char*>(fileBuf.data() + currentIdx), fullSuffix.c_str());
if (res == nullptr) {
return returnvalue::FAILED;
}
currentIdx += fullSuffix.size();
}
path newPath(std::string(reinterpret_cast<const char*>(fileBuf.data()), currentIdx));
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;
@ -354,3 +432,33 @@ void PersistentTmStore::getStartAndEndTimeCurrentOrLastDump(uint32_t& startTime,
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;
struct tm* time = gmtime(&epoch);
size_t writtenBytes = strftime(reinterpret_cast<char*>(filePathBuf.data() + currentIdx),
filePathBuf.size(), config::FILE_DATE_FORMAT, time);
if (writtenBytes == 0) {
sif::error << "PersistentTmStore::createMostRecentFile: Could not create file timestamp"
<< std::endl;
return returnvalue::FAILED;
}
currentIdx += writtenBytes;
char* res = strcpy(reinterpret_cast<char*>(filePathBuf.data() + currentIdx), ".bin");
if (res == nullptr) {
return returnvalue::FAILED;
}
currentIdx += 4;
if (suffix.has_value()) {
std::string fullSuffix = "." + std::to_string(suffix.value());
res = strcpy(reinterpret_cast<char*>(filePathBuf.data() + currentIdx), fullSuffix.c_str());
if (res == nullptr) {
return returnvalue::FAILED;
}
currentIdx += fullSuffix.size();
}
fullPathLength = currentIdx;
return returnvalue::OK;
}

View File

@ -10,6 +10,7 @@
#include <mission/memory/SdCardMountedIF.h>
#include <filesystem>
#include <set>
#include "eive/eventSubsystemIds.h"
#include "eive/resultClassIds.h"
@ -37,6 +38,14 @@ struct PersistentTmStoreArgs {
SdCardMountedIF& sdcMan;
};
struct DumpIndex {
uint32_t epoch = 0;
// Number of additional files with a suffix like .0, .1 etc.
uint8_t additionalFiles = 0;
// Define a custom comparison function based on the epoch variable
bool operator<(const DumpIndex& other) const { return epoch < other.epoch; }
};
class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
public:
enum class State { IDLE, DUMPING };
@ -96,7 +105,10 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
std::string baseName;
uint8_t currentSameSecNumber = 0;
std::filesystem::path basePath;
// std::filesystem::path pathStart = basePath / baseName;
uint32_t rolloverDiffSeconds = 0;
std::array<uint8_t, 524> filePathBuf{};
size_t basePathSize;
std::array<uint8_t, MAX_FILESIZE> fileBuf{};
timeval currentTv;
timeval activeFileTv{};
@ -106,8 +118,10 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
uint32_t fromUnixTime = 0;
uint32_t untilUnixTime = 0;
uint32_t currentFileUnixStamp = 0;
std::filesystem::directory_iterator dirIter;
std::filesystem::directory_entry dirEntry;
std::filesystem::path currentFile;
std::set<DumpIndex> orderedDumpFilestamps{};
std::set<DumpIndex>::iterator dumpIter;
std::optional<uint8_t> currentSameFileIdx = 0;
size_t fileSize = 0;
size_t currentSize = 0;
};
@ -122,10 +136,13 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
[[nodiscard]] MessageQueueId_t getCommandQueue() const override;
void calcDiffSeconds(RolloverInterval intervalUnit, uint32_t intervalCount);
ReturnValue_t buildDumpSet(uint32_t fromUnixSeconds, uint32_t upToUnixSeconds);
ReturnValue_t createMostRecentFile(std::optional<uint8_t> suffix);
static ReturnValue_t pathToTime(const std::filesystem::path& path, struct tm& time);
void fileToPackets(const std::filesystem::path& path, uint32_t unixStamp);
ReturnValue_t loadNextDumpFile();
ReturnValue_t createFileName(timeval& tv, std::optional<uint8_t> suffix, size_t& fullPathLength);
std::optional<uint8_t> extractSuffix(const std::string& pathStr);
bool updateBaseDir();
ReturnValue_t assignAndOrCreateMostRecentFile();
};

2
tmtc

@ -1 +1 @@
Subproject commit c9f4a8070d20bc659809d5b822ac5a17548f57a4
Subproject commit ec0ebc365308198046addc94909b1bca8678aa5a

View File

@ -4,7 +4,7 @@ add_subdirectory(mocks)
target_sources(${UNITTEST_NAME} PRIVATE
main.cpp
testEnvironment.cpp
testStampInFilename.cpp
testGenericFilesystem.cpp
hdlcEncodingRw.cpp
printChar.cpp
)

View File

@ -19,116 +19,117 @@ CoreController::CoreController() {
setCurrentBootCopy(xsc::CHIP_0, xsc::COPY_0);
}
void CoreController::performRebootFileHandling(bool recreateFile) {
void CoreController::performRebootWatchdogHandling(bool recreateFile) {
using namespace std;
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE;
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE;
if (not std::filesystem::exists(path) or recreateFile) {
#if OBSW_VERBOSE_LEVEL >= 1
sif::info << "CoreController::performRebootFileHandling: Recreating reboot file" << std::endl;
#endif
rebootFile.enabled = true;
rebootFile.img00Cnt = 0;
rebootFile.img01Cnt = 0;
rebootFile.img10Cnt = 0;
rebootFile.img11Cnt = 0;
rebootFile.lastChip = xsc::Chip::CHIP_0;
rebootFile.lastCopy = xsc::Copy::COPY_0;
rebootFile.img00Lock = false;
rebootFile.img01Lock = false;
rebootFile.img10Lock = false;
rebootFile.img11Lock = false;
rebootFile.mechanismNextChip = xsc::Chip::NO_CHIP;
rebootFile.mechanismNextCopy = xsc::Copy::NO_COPY;
rebootFile.bootFlag = false;
rewriteRebootFile(rebootFile);
rebootWatchdogFile.enabled = true;
rebootWatchdogFile.img00Cnt = 0;
rebootWatchdogFile.img01Cnt = 0;
rebootWatchdogFile.img10Cnt = 0;
rebootWatchdogFile.img11Cnt = 0;
rebootWatchdogFile.lastChip = xsc::Chip::CHIP_0;
rebootWatchdogFile.lastCopy = xsc::Copy::COPY_0;
rebootWatchdogFile.img00Lock = false;
rebootWatchdogFile.img01Lock = false;
rebootWatchdogFile.img10Lock = false;
rebootWatchdogFile.img11Lock = false;
rebootWatchdogFile.mechanismNextChip = xsc::Chip::NO_CHIP;
rebootWatchdogFile.mechanismNextCopy = xsc::Copy::NO_COPY;
rebootWatchdogFile.bootFlag = false;
rewriteRebootWatchdogFile(rebootWatchdogFile);
} else {
if (not parseRebootFile(path, rebootFile)) {
performRebootFileHandling(true);
if (not parseRebootWatchdogFile(path, rebootWatchdogFile)) {
performRebootWatchdogHandling(true);
}
}
if (CURRENT_CHIP == xsc::CHIP_0) {
if (CURRENT_COPY == xsc::COPY_0) {
rebootFile.img00Cnt++;
rebootWatchdogFile.img00Cnt++;
} else {
rebootFile.img01Cnt++;
rebootWatchdogFile.img01Cnt++;
}
} else {
if (CURRENT_COPY == xsc::COPY_0) {
rebootFile.img10Cnt++;
rebootWatchdogFile.img10Cnt++;
} else {
rebootFile.img11Cnt++;
rebootWatchdogFile.img11Cnt++;
}
}
if (rebootFile.bootFlag) {
if (rebootWatchdogFile.bootFlag) {
// Trigger event to inform ground that a reboot was triggered
uint32_t p1 = rebootFile.lastChip << 16 | rebootFile.lastCopy;
uint32_t p2 = rebootFile.img00Cnt << 24 | rebootFile.img01Cnt << 16 | rebootFile.img10Cnt << 8 |
rebootFile.img11Cnt;
uint32_t p1 = rebootWatchdogFile.lastChip << 16 | rebootWatchdogFile.lastCopy;
uint32_t p2 = rebootWatchdogFile.img00Cnt << 24 | rebootWatchdogFile.img01Cnt << 16 |
rebootWatchdogFile.img10Cnt << 8 | rebootWatchdogFile.img11Cnt;
triggerEvent(REBOOT_MECHANISM_TRIGGERED, p1, p2);
// Clear the boot flag
rebootFile.bootFlag = false;
rebootWatchdogFile.bootFlag = false;
}
if (rebootFile.mechanismNextChip != xsc::NO_CHIP and
rebootFile.mechanismNextCopy != xsc::NO_COPY) {
if (CURRENT_CHIP != rebootFile.mechanismNextChip or
CURRENT_COPY != rebootFile.mechanismNextCopy) {
std::string infoString = std::to_string(rebootFile.mechanismNextChip) + " " +
std::to_string(rebootFile.mechanismNextCopy);
if (rebootWatchdogFile.mechanismNextChip != xsc::NO_CHIP and
rebootWatchdogFile.mechanismNextCopy != xsc::NO_COPY) {
if (CURRENT_CHIP != rebootWatchdogFile.mechanismNextChip or
CURRENT_COPY != rebootWatchdogFile.mechanismNextCopy) {
std::string infoString = std::to_string(rebootWatchdogFile.mechanismNextChip) + " " +
std::to_string(rebootWatchdogFile.mechanismNextCopy);
sif::warning << "CoreController::performRebootFileHandling: Expected to be on image "
<< infoString << " but currently on other image. Locking " << infoString
<< std::endl;
// Firmware or other component might be corrupt and we are on another image then the target
// image specified by the mechanism. We can't really trust the target image anymore.
// Lock it for now
if (rebootFile.mechanismNextChip == xsc::CHIP_0) {
if (rebootFile.mechanismNextCopy == xsc::COPY_0) {
rebootFile.img00Lock = true;
if (rebootWatchdogFile.mechanismNextChip == xsc::CHIP_0) {
if (rebootWatchdogFile.mechanismNextCopy == xsc::COPY_0) {
rebootWatchdogFile.img00Lock = true;
} else {
rebootFile.img01Lock = true;
rebootWatchdogFile.img01Lock = true;
}
} else {
if (rebootFile.mechanismNextCopy == xsc::COPY_0) {
rebootFile.img10Lock = true;
if (rebootWatchdogFile.mechanismNextCopy == xsc::COPY_0) {
rebootWatchdogFile.img10Lock = true;
} else {
rebootFile.img11Lock = true;
rebootWatchdogFile.img11Lock = true;
}
}
}
}
rebootFile.lastChip = CURRENT_CHIP;
rebootFile.lastCopy = CURRENT_COPY;
rebootWatchdogFile.lastChip = CURRENT_CHIP;
rebootWatchdogFile.lastCopy = CURRENT_COPY;
// Only reboot if the reboot functionality is enabled.
// The handler will still increment the boot counts
if (rebootFile.enabled and (*rebootFile.relevantBootCnt >= rebootFile.maxCount)) {
if (rebootWatchdogFile.enabled and
(*rebootWatchdogFile.relevantBootCnt >= rebootWatchdogFile.maxCount)) {
// Reboot to other image
bool doReboot = false;
xsc::Chip tgtChip = xsc::NO_CHIP;
xsc::Copy tgtCopy = xsc::NO_COPY;
determineAndExecuteReboot(rebootFile, doReboot, tgtChip, tgtCopy);
determineAndExecuteReboot(rebootWatchdogFile, doReboot, tgtChip, tgtCopy);
if (doReboot) {
rebootFile.bootFlag = true;
rebootWatchdogFile.bootFlag = true;
#if OBSW_VERBOSE_LEVEL >= 1
sif::info << "Boot counter for image " << CURRENT_CHIP << " " << CURRENT_COPY
<< " too high. Rebooting to " << tgtChip << " " << tgtCopy << std::endl;
#endif
rebootFile.mechanismNextChip = tgtChip;
rebootFile.mechanismNextCopy = tgtCopy;
rewriteRebootFile(rebootFile);
rebootWatchdogFile.mechanismNextChip = tgtChip;
rebootWatchdogFile.mechanismNextCopy = tgtCopy;
rewriteRebootWatchdogFile(rebootWatchdogFile);
xsc_boot_copy(static_cast<xsc_libnor_chip_t>(tgtChip),
static_cast<xsc_libnor_copy_t>(tgtCopy));
}
} else {
rebootFile.mechanismNextChip = xsc::NO_CHIP;
rebootFile.mechanismNextCopy = xsc::NO_COPY;
rebootWatchdogFile.mechanismNextChip = xsc::NO_CHIP;
rebootWatchdogFile.mechanismNextCopy = xsc::NO_COPY;
}
rewriteRebootFile(rebootFile);
rewriteRebootWatchdogFile(rebootWatchdogFile);
}
void CoreController::determineAndExecuteReboot(RebootFile &rf, bool &needsReboot,
void CoreController::determineAndExecuteReboot(RebootWatchdogFile &rf, bool &needsReboot,
xsc::Chip &tgtChip, xsc::Copy &tgtCopy) {
tgtChip = xsc::CHIP_0;
tgtCopy = xsc::COPY_0;
@ -217,7 +218,7 @@ void CoreController::determineAndExecuteReboot(RebootFile &rf, bool &needsReboot
}
}
bool CoreController::parseRebootFile(std::string path, RebootFile &rf) {
bool CoreController::parseRebootWatchdogFile(std::string path, RebootWatchdogFile &rf) {
using namespace std;
std::string selfMatch;
if (CURRENT_CHIP == xsc::CHIP_0) {
@ -400,34 +401,34 @@ bool CoreController::parseRebootFile(std::string path, RebootFile &rf) {
}
void CoreController::resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy) {
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE;
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE;
// Disable the reboot file mechanism
parseRebootFile(path, rebootFile);
parseRebootWatchdogFile(path, rebootWatchdogFile);
if (tgtChip == xsc::ALL_CHIP and tgtCopy == xsc::ALL_COPY) {
rebootFile.img00Cnt = 0;
rebootFile.img01Cnt = 0;
rebootFile.img10Cnt = 0;
rebootFile.img11Cnt = 0;
rebootWatchdogFile.img00Cnt = 0;
rebootWatchdogFile.img01Cnt = 0;
rebootWatchdogFile.img10Cnt = 0;
rebootWatchdogFile.img11Cnt = 0;
} else {
if (tgtChip == xsc::CHIP_0) {
if (tgtCopy == xsc::COPY_0) {
rebootFile.img00Cnt = 0;
rebootWatchdogFile.img00Cnt = 0;
} else {
rebootFile.img01Cnt = 0;
rebootWatchdogFile.img01Cnt = 0;
}
} else {
if (tgtCopy == xsc::COPY_0) {
rebootFile.img10Cnt = 0;
rebootWatchdogFile.img10Cnt = 0;
} else {
rebootFile.img11Cnt = 0;
rebootWatchdogFile.img11Cnt = 0;
}
}
}
rewriteRebootFile(rebootFile);
rewriteRebootWatchdogFile(rebootWatchdogFile);
}
void CoreController::rewriteRebootFile(RebootFile file) {
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE;
void CoreController::rewriteRebootWatchdogFile(RebootWatchdogFile file) {
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE;
std::ofstream rebootFile(path);
if (rebootFile.is_open()) {
// Initiate reboot file first. Reboot handling will be on on initialization
@ -450,15 +451,15 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
if (size < 1) {
return HasActionsIF::INVALID_PARAMETERS;
}
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE;
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE;
// Disable the reboot file mechanism
parseRebootFile(path, rebootFile);
parseRebootWatchdogFile(path, rebootWatchdogFile);
if (data[0] == 0) {
rebootFile.enabled = false;
rewriteRebootFile(rebootFile);
rebootWatchdogFile.enabled = false;
rewriteRebootWatchdogFile(rebootWatchdogFile);
} else if (data[0] == 1) {
rebootFile.enabled = true;
rewriteRebootFile(rebootFile);
rebootWatchdogFile.enabled = true;
rewriteRebootWatchdogFile(rebootWatchdogFile);
} else {
return HasActionsIF::INVALID_PARAMETERS;
}
@ -490,11 +491,11 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
if (size < 1) {
return HasActionsIF::INVALID_PARAMETERS;
}
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE;
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE;
// Disable the reboot file mechanism
parseRebootFile(path, rebootFile);
rebootFile.maxCount = data[0];
rewriteRebootFile(rebootFile);
parseRebootWatchdogFile(path, rebootWatchdogFile);
rebootWatchdogFile.maxCount = data[0];
rewriteRebootWatchdogFile(rebootWatchdogFile);
return HasActionsIF::EXECUTION_FINISHED;
}
default: {
@ -504,23 +505,23 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
}
void CoreController::setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::Copy tgtCopy) {
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE;
std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE;
// Disable the reboot file mechanism
parseRebootFile(path, rebootFile);
parseRebootWatchdogFile(path, rebootWatchdogFile);
if (tgtChip == xsc::CHIP_0) {
if (tgtCopy == xsc::COPY_0) {
rebootFile.img00Lock = lock;
rebootWatchdogFile.img00Lock = lock;
} else {
rebootFile.img01Lock = lock;
rebootWatchdogFile.img01Lock = lock;
}
} else {
if (tgtCopy == xsc::COPY_0) {
rebootFile.img10Lock = lock;
rebootWatchdogFile.img10Lock = lock;
} else {
rebootFile.img11Lock = lock;
rebootWatchdogFile.img11Lock = lock;
}
}
rewriteRebootFile(rebootFile);
rewriteRebootWatchdogFile(rebootWatchdogFile);
}
void CoreController::setCurrentBootCopy(xsc::Chip chip, xsc::Copy copy) {

View File

@ -14,7 +14,7 @@ enum Copy : int { COPY_0, COPY_1, NO_COPY, SELF_COPY, ALL_COPY };
} // namespace xsc
struct RebootFile {
struct RebootWatchdogFile {
static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10;
bool enabled = true;
@ -39,7 +39,7 @@ class SdCardManager;
class CoreController {
public:
static constexpr char REBOOT_FILE[] = "/conf/reboot.txt";
static constexpr char REBOOT_WATCHDOG_FILE[] = "/conf/reboot.txt";
//! [EXPORT] : [COMMENT] The reboot mechanism was triggered.
//! P1: First 16 bits: Last Chip, Last 16 bits: Last Copy,
//! P2: Each byte is the respective reboot count for the slots
@ -57,13 +57,13 @@ class CoreController {
static void setCurrentBootCopy(xsc::Chip chip, xsc::Copy copy);
ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
const uint8_t* data, size_t size);
void performRebootFileHandling(bool recreateFile);
void determineAndExecuteReboot(RebootFile& rf, bool& needsReboot, xsc::Chip& tgtChip,
void performRebootWatchdogHandling(bool recreateFile);
void determineAndExecuteReboot(RebootWatchdogFile& rf, bool& needsReboot, xsc::Chip& tgtChip,
xsc::Copy& tgtCopy);
void setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::Copy tgtCopy);
void resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy);
bool parseRebootFile(std::string path, RebootFile& file);
void rewriteRebootFile(RebootFile file);
bool parseRebootWatchdogFile(std::string path, RebootWatchdogFile& file);
void rewriteRebootWatchdogFile(RebootWatchdogFile file);
private:
struct SdFsmParams {
@ -74,6 +74,6 @@ class CoreController {
} sdInfo;
SdCardManager* sdcMan = nullptr;
RebootFile rebootFile = {};
RebootWatchdogFile rebootWatchdogFile = {};
bool doPerformRebootFileHandling = true;
};

View File

@ -27,7 +27,7 @@ void factory(void* args) {
new HouseKeepingMock();
eventManager = new EventManagerMock();
new HealthTable(objects::HEALTH_TABLE);
new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER);
new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER, 5, false, 60.0);
new CdsShortTimeStamper(objects::TIME_STAMPER);
{

View File

@ -0,0 +1,43 @@
#include <catch2/catch_test_macros.hpp>
#include <cinttypes>
#include "fsfw/timemanager/Clock.h"
uint8_t extractSuffix(const std::string& pathStr) {
std::string numberStr;
// Find the position of the dot at the end of the file path
size_t dotPos = pathStr.find_last_of('.');
if (dotPos != std::string::npos && dotPos < pathStr.length() - 1) {
// Extract the substring after the dot
numberStr = pathStr.substr(dotPos + 1);
}
int number = std::stoi(numberStr);
if (number < 0 or number > std::numeric_limits<uint8_t>::max()) {
return 0;
}
return static_cast<uint8_t>(number);
}
TEST_CASE("Stamp in Filename", "[Stamp In Filename]") {
Clock::TimeOfDay_t tod;
std::string baseName = "verif";
std::string pathStr = "verif_2022-05-25T16:55:23Z.bin";
unsigned int underscorePos = pathStr.find_last_of('_');
std::string stampStr = pathStr.substr(underscorePos + 1);
float seconds = 0.0;
char* prefix = nullptr;
int count =
sscanf(stampStr.c_str(),
"%4" SCNu32 "-%2" SCNu32 "-%2" SCNu32 "T%2" SCNu32 ":%2" SCNu32 ":%2" SCNu32 "Z",
&tod.year, &tod.month, &tod.day, &tod.hour, &tod.minute, &tod.second);
static_cast<void>(count);
CHECK(count == 6);
}
TEST_CASE("Suffix Extraction") {
std::string pathStr = "/mnt/sd0/tm/hk/hk-some-stamp.bin.0";
CHECK(extractSuffix(pathStr) == 0);
pathStr = "/mnt/sd0/tm/hk/hk-some-stamp.bin.2";
CHECK(extractSuffix(pathStr) == 2);
}

View File

@ -1,21 +0,0 @@
#include <catch2/catch_test_macros.hpp>
#include <cinttypes>
#include "fsfw/timemanager/Clock.h"
TEST_CASE("Stamp in Filename", "[Stamp In Filename]") {
Clock::TimeOfDay_t tod;
std::string baseName = "verif";
std::string pathStr = "verif_2022-05-25T16:55:23Z.bin";
unsigned int underscorePos = pathStr.find_last_of('_');
std::string stampStr = pathStr.substr(underscorePos + 1);
float seconds = 0.0;
char* prefix = nullptr;
int count =
sscanf(stampStr.c_str(),
"%4" SCNu32 "-%2" SCNu32 "-%2" SCNu32 "T%2" SCNu32 ":%2" SCNu32 ":%2" SCNu32 "Z",
&tod.year, &tod.month, &tod.day, &tod.hour, &tod.minute, &tod.second);
static_cast<void>(count);
CHECK(count == 6);
}