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] # [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 # [v4.0.1] 2023-06-24
## Fixed ## Fixed

View File

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

View File

@ -68,7 +68,7 @@ void ObjectFactory::produce(void* args) {
#endif #endif
auto sdcMan = new DummySdCardManager("/tmp"); auto sdcMan = new DummySdCardManager("/tmp");
ObjectFactory::produceGenericObjects(nullptr, &pusFunnel, &cfdpFunnel, *sdcMan, &ipcStore, ObjectFactory::produceGenericObjects(nullptr, &pusFunnel, &cfdpFunnel, *sdcMan, &ipcStore,
&tmStore, persistentStores, 120); &tmStore, persistentStores, 120, enableHkSets);
new TmFunnelHandler(objects::LIVE_TM_TASK, *pusFunnel, *cfdpFunnel); new TmFunnelHandler(objects::LIVE_TM_TASK, *pusFunnel, *cfdpFunnel);
auto* dummyGpioIF = new DummyGpioIF(); 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 RAD_SENSOR_CHIP_SELECT[] = "rad_sensor_chip_select";
static constexpr char ENABLE_RADFET[] = "enable_radfet"; 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_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_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_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 PAPB_EMPTY_SIGNAL_VC3[] = "papb_empty_signal_vc3";
static constexpr char PTME_RESETN[] = "ptme_resetn"; 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; 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); UioMapper sysRomMapper(q7s::UIO_SYS_ROM);
result = sysRomMapper.getMappedAdress(&mappedSysRomAddr, UioMapper::Permissions::READ_ONLY); result = sysRomMapper.getMappedAdress(&mappedSysRomAddr, UioMapper::Permissions::READ_ONLY);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
@ -197,6 +198,7 @@ ReturnValue_t CoreController::initialize() {
sif::error << "Getting mapped SYS ROM UIO address failed" << std::endl; sif::error << "Getting mapped SYS ROM UIO address failed" << std::endl;
return ObjectManager::CHILD_INIT_FAILED; return ObjectManager::CHILD_INIT_FAILED;
} }
}
return returnvalue::OK; return returnvalue::OK;
} }
@ -321,23 +323,23 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
if (size < 1) { if (size < 1) {
return HasActionsIF::INVALID_PARAMETERS; return HasActionsIF::INVALID_PARAMETERS;
} }
std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_FILE; std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_WATCHDOG_FILE;
parseRebootFile(path, rebootFile); parseRebootWatchdogFile(path, rebootWatchdogFile);
if (data[0] == 0) { if (data[0] == 0) {
rebootFile.enabled = false; rebootWatchdogFile.enabled = false;
rewriteRebootFile(rebootFile); rewriteRebootWatchdogFile(rebootWatchdogFile);
} else if (data[0] == 1) { } else if (data[0] == 1) {
rebootFile.enabled = true; rebootWatchdogFile.enabled = true;
rewriteRebootFile(rebootFile); rewriteRebootWatchdogFile(rebootWatchdogFile);
} else { } else {
return HasActionsIF::INVALID_PARAMETERS; return HasActionsIF::INVALID_PARAMETERS;
} }
return HasActionsIF::EXECUTION_FINISHED; return HasActionsIF::EXECUTION_FINISHED;
} }
case (READ_REBOOT_MECHANISM_INFO): { case (READ_REBOOT_MECHANISM_INFO): {
std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_FILE; std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_WATCHDOG_FILE;
parseRebootFile(path, rebootFile); parseRebootWatchdogFile(path, rebootWatchdogFile);
RebootMechanismPacket packet(rebootFile); RebootWatchdogPacket packet(rebootWatchdogFile);
ReturnValue_t result = actionHelper.reportData(commandedBy, actionId, &packet); ReturnValue_t result = actionHelper.reportData(commandedBy, actionId, &packet);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
return result; return result;
@ -346,12 +348,13 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
} }
case (RESET_REBOOT_COUNTERS): { case (RESET_REBOOT_COUNTERS): {
if (size == 0) { if (size == 0) {
resetRebootCount(xsc::ALL_CHIP, xsc::ALL_COPY); resetRebootWatchdogCounters(xsc::ALL_CHIP, xsc::ALL_COPY);
} else if (size == 2) { } else if (size == 2) {
if (data[0] > 1 or data[1] > 1) { if (data[0] > 1 or data[1] > 1) {
return HasActionsIF::INVALID_PARAMETERS; 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; return HasActionsIF::EXECUTION_FINISHED;
} }
@ -446,11 +449,11 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
if (size < 1) { if (size < 1) {
return HasActionsIF::INVALID_PARAMETERS; return HasActionsIF::INVALID_PARAMETERS;
} }
std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_FILE; std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_WATCHDOG_FILE;
// Disable the reboot file mechanism // Disable the reboot file mechanism
parseRebootFile(path, rebootFile); parseRebootWatchdogFile(path, rebootWatchdogFile);
rebootFile.maxCount = data[0]; rebootWatchdogFile.maxCount = data[0];
rewriteRebootFile(rebootFile); rewriteRebootWatchdogFile(rebootWatchdogFile);
return HasActionsIF::EXECUTION_FINISHED; return HasActionsIF::EXECUTION_FINISHED;
} }
case (XSC_REBOOT_OBC): { 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 tgtChip = static_cast<xsc::Chip>(data[1]);
auto tgtCopy = static_cast<xsc::Copy>(data[2]); auto tgtCopy = static_cast<xsc::Copy>(data[2]);
// This function can not really fail performGracefulShutdown(tgtChip, tgtCopy);
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::FAILED; return returnvalue::FAILED;
} }
@ -1263,10 +1228,15 @@ ReturnValue_t CoreController::gracefulShutdownTasks(xsc::Chip chip, xsc::Copy co
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
TaskFactory::delayTask(2000); TaskFactory::delayTask(2000);
// Ensure that all writes/reads do finish.
sync();
// Attempt graceful shutdown by unmounting and switching off SD cards // Attempt graceful shutdown by unmounting and switching off SD cards
sdcMan->switchOffSdCard(sd::SdCard::SLOT_0); sdcMan->switchOffSdCard(sd::SdCard::SLOT_0);
sdcMan->switchOffSdCard(sd::SdCard::SLOT_1); 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, ReturnValue_t result = setBootCopyProtection(xsc::Chip::SELF_CHIP, xsc::Copy::SELF_COPY, true,
protOpPerformed, false); protOpPerformed, false);
if (result == returnvalue::OK and protOpPerformed) { if (result == returnvalue::OK and protOpPerformed) {
@ -1555,7 +1525,8 @@ void CoreController::performMountedSdCardOperations() {
if (not timeFileInitDone) { if (not timeFileInitDone) {
initClockFromTimeFile(); initClockFromTimeFile();
} }
performRebootFileHandling(false); performRebootWatchdogHandling(false);
performRebootCountersHandling(false);
} }
backupTimeFileHandler(); backupTimeFileHandler();
}; };
@ -1627,117 +1598,126 @@ ReturnValue_t CoreController::performSdCardCheck() {
return returnvalue::OK; return returnvalue::OK;
} }
void CoreController::performRebootFileHandling(bool recreateFile) { void CoreController::performRebootWatchdogHandling(bool recreateFile) {
using namespace std; 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; 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 (not std::filesystem::exists(path, e) or recreateFile) {
#if OBSW_VERBOSE_LEVEL >= 1 #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 #endif
rebootFile.enabled = false; rebootWatchdogFile.enabled = false;
rebootFile.img00Cnt = 0; rebootWatchdogFile.img00Cnt = 0;
rebootFile.img01Cnt = 0; rebootWatchdogFile.img01Cnt = 0;
rebootFile.img10Cnt = 0; rebootWatchdogFile.img10Cnt = 0;
rebootFile.img11Cnt = 0; rebootWatchdogFile.img11Cnt = 0;
rebootFile.lastChip = xsc::Chip::CHIP_0; rebootWatchdogFile.lastChip = xsc::Chip::CHIP_0;
rebootFile.lastCopy = xsc::Copy::COPY_0; rebootWatchdogFile.lastCopy = xsc::Copy::COPY_0;
rebootFile.img00Lock = false; rebootWatchdogFile.img00Lock = false;
rebootFile.img01Lock = false; rebootWatchdogFile.img01Lock = false;
rebootFile.img10Lock = false; rebootWatchdogFile.img10Lock = false;
rebootFile.img11Lock = false; rebootWatchdogFile.img11Lock = false;
rebootFile.mechanismNextChip = xsc::Chip::NO_CHIP; rebootWatchdogFile.mechanismNextChip = xsc::Chip::NO_CHIP;
rebootFile.mechanismNextCopy = xsc::Copy::NO_COPY; rebootWatchdogFile.mechanismNextCopy = xsc::Copy::NO_COPY;
rebootFile.bootFlag = false; rebootWatchdogFile.bootFlag = false;
rewriteRebootFile(rebootFile); rewriteRebootWatchdogFile(rebootWatchdogFile);
} else { } else {
if (not parseRebootFile(path, rebootFile)) { if (not parseRebootWatchdogFile(path, rebootWatchdogFile)) {
performRebootFileHandling(true); performRebootWatchdogHandling(true);
return;
} }
} }
if (CURRENT_CHIP == xsc::CHIP_0) { if (CURRENT_CHIP == xsc::CHIP_0) {
if (CURRENT_COPY == xsc::COPY_0) { if (CURRENT_COPY == xsc::COPY_0) {
rebootFile.img00Cnt++; rebootWatchdogFile.img00Cnt++;
} else { } else {
rebootFile.img01Cnt++; rebootWatchdogFile.img01Cnt++;
} }
} else { } else {
if (CURRENT_COPY == xsc::COPY_0) { if (CURRENT_COPY == xsc::COPY_0) {
rebootFile.img10Cnt++; rebootWatchdogFile.img10Cnt++;
} else { } else {
rebootFile.img11Cnt++; rebootWatchdogFile.img11Cnt++;
} }
} }
if (rebootFile.bootFlag) { if (rebootWatchdogFile.bootFlag) {
// Trigger event to inform ground that a reboot was triggered // 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); triggerEvent(core::REBOOT_MECHANISM_TRIGGERED, p1, 0);
// Clear the boot flag // Clear the boot flag
rebootFile.bootFlag = false; rebootWatchdogFile.bootFlag = false;
} }
announceBootCounts(); if (rebootWatchdogFile.mechanismNextChip != xsc::NO_CHIP and
rebootWatchdogFile.mechanismNextCopy != xsc::NO_COPY) {
if (rebootFile.mechanismNextChip != xsc::NO_CHIP and if (CURRENT_CHIP != rebootWatchdogFile.mechanismNextChip or
rebootFile.mechanismNextCopy != xsc::NO_COPY) { CURRENT_COPY != rebootWatchdogFile.mechanismNextCopy) {
if (CURRENT_CHIP != rebootFile.mechanismNextChip or std::string infoString = std::to_string(rebootWatchdogFile.mechanismNextChip) + " " +
CURRENT_COPY != rebootFile.mechanismNextCopy) { std::to_string(rebootWatchdogFile.mechanismNextCopy);
std::string infoString = std::to_string(rebootFile.mechanismNextChip) + " " +
std::to_string(rebootFile.mechanismNextCopy);
sif::warning << "CoreController::performRebootFileHandling: Expected to be on image " sif::warning << "CoreController::performRebootFileHandling: Expected to be on image "
<< infoString << " but currently on other image. Locking " << infoString << infoString << " but currently on other image. Locking " << infoString
<< std::endl; << std::endl;
// Firmware or other component might be corrupt and we are on another image then the target // 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. // image specified by the mechanism. We can't really trust the target image anymore.
// Lock it for now // Lock it for now
if (rebootFile.mechanismNextChip == xsc::CHIP_0) { if (rebootWatchdogFile.mechanismNextChip == xsc::CHIP_0) {
if (rebootFile.mechanismNextCopy == xsc::COPY_0) { if (rebootWatchdogFile.mechanismNextCopy == xsc::COPY_0) {
rebootFile.img00Lock = true; rebootWatchdogFile.img00Lock = true;
} else { } else {
rebootFile.img01Lock = true; rebootWatchdogFile.img01Lock = true;
} }
} else { } else {
if (rebootFile.mechanismNextCopy == xsc::COPY_0) { if (rebootWatchdogFile.mechanismNextCopy == xsc::COPY_0) {
rebootFile.img10Lock = true; rebootWatchdogFile.img10Lock = true;
} else { } else {
rebootFile.img11Lock = true; rebootWatchdogFile.img11Lock = true;
} }
} }
} }
} }
rebootFile.lastChip = CURRENT_CHIP; rebootWatchdogFile.lastChip = CURRENT_CHIP;
rebootFile.lastCopy = CURRENT_COPY; rebootWatchdogFile.lastCopy = CURRENT_COPY;
// Only reboot if the reboot functionality is enabled. // Only reboot if the reboot functionality is enabled.
// The handler will still increment the boot counts // 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 // Reboot to other image
bool doReboot = false; bool doReboot = false;
xsc::Chip tgtChip = xsc::NO_CHIP; xsc::Chip tgtChip = xsc::NO_CHIP;
xsc::Copy tgtCopy = xsc::NO_COPY; xsc::Copy tgtCopy = xsc::NO_COPY;
determineAndExecuteReboot(rebootFile, doReboot, tgtChip, tgtCopy); rebootWatchdogAlgorithm(rebootWatchdogFile, doReboot, tgtChip, tgtCopy);
if (doReboot) { if (doReboot) {
rebootFile.bootFlag = true; rebootWatchdogFile.bootFlag = true;
#if OBSW_VERBOSE_LEVEL >= 1 #if OBSW_VERBOSE_LEVEL >= 1
sif::info << "Boot counter for image " << CURRENT_CHIP << " " << CURRENT_COPY sif::info << "Boot counter for image " << CURRENT_CHIP << " " << CURRENT_COPY
<< " too high. Rebooting to " << tgtChip << " " << tgtCopy << std::endl; << " too high. Rebooting to " << tgtChip << " " << tgtCopy << std::endl;
#endif #endif
rebootFile.mechanismNextChip = tgtChip; rebootWatchdogFile.mechanismNextChip = tgtChip;
rebootFile.mechanismNextCopy = tgtCopy; rebootWatchdogFile.mechanismNextCopy = tgtCopy;
rewriteRebootFile(rebootFile); rewriteRebootWatchdogFile(rebootWatchdogFile);
xsc_boot_copy(static_cast<xsc_libnor_chip_t>(tgtChip), performGracefulShutdown(tgtChip, tgtCopy);
static_cast<xsc_libnor_copy_t>(tgtCopy));
} }
} else { } else {
rebootFile.mechanismNextChip = xsc::NO_CHIP; rebootWatchdogFile.mechanismNextChip = xsc::NO_CHIP;
rebootFile.mechanismNextCopy = xsc::NO_COPY; 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) { xsc::Chip &tgtChip, xsc::Copy &tgtCopy) {
tgtChip = xsc::CHIP_0; tgtChip = xsc::CHIP_0;
tgtCopy = xsc::COPY_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; using namespace std;
std::string selfMatch; std::string selfMatch;
if (CURRENT_CHIP == xsc::CHIP_0) { if (CURRENT_CHIP == xsc::CHIP_0) {
@ -2008,34 +1988,123 @@ bool CoreController::parseRebootFile(std::string path, RebootFile &rf) {
return true; return true;
} }
void CoreController::resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy) { bool CoreController::parseRebootCountersFile(std::string path, RebootCountersFile &rf) {
std::string path = currMntPrefix + REBOOT_FILE; using namespace std;
parseRebootFile(path, rebootFile); 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) { if (tgtChip == xsc::ALL_CHIP and tgtCopy == xsc::ALL_COPY) {
rebootFile.img00Cnt = 0; rebootWatchdogFile.img00Cnt = 0;
rebootFile.img01Cnt = 0; rebootWatchdogFile.img01Cnt = 0;
rebootFile.img10Cnt = 0; rebootWatchdogFile.img10Cnt = 0;
rebootFile.img11Cnt = 0; rebootWatchdogFile.img11Cnt = 0;
} else { } else {
if (tgtChip == xsc::CHIP_0) { if (tgtChip == xsc::CHIP_0) {
if (tgtCopy == xsc::COPY_0) { if (tgtCopy == xsc::COPY_0) {
rebootFile.img00Cnt = 0; rebootWatchdogFile.img00Cnt = 0;
} else { } else {
rebootFile.img01Cnt = 0; rebootWatchdogFile.img01Cnt = 0;
} }
} else { } else {
if (tgtCopy == xsc::COPY_0) { if (tgtCopy == xsc::COPY_0) {
rebootFile.img10Cnt = 0; rebootWatchdogFile.img10Cnt = 0;
} else { } else {
rebootFile.img11Cnt = 0; rebootWatchdogFile.img11Cnt = 0;
} }
} }
} }
rewriteRebootFile(rebootFile); rewriteRebootWatchdogFile(rebootWatchdogFile);
} }
void CoreController::rewriteRebootFile(RebootFile file) { void CoreController::performRebootCountersHandling(bool recreateFile) {
std::string path = currMntPrefix + REBOOT_FILE; 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); std::ofstream rebootFile(path);
if (rebootFile.is_open()) { if (rebootFile.is_open()) {
// Initiate reboot file first. Reboot handling will be on on initialization // 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) << " " << "\nnext: " << static_cast<int>(file.mechanismNextChip) << " "
<< static_cast<int>(file.mechanismNextCopy) << "\n"; << 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) { void CoreController::setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::Copy tgtCopy) {
std::string path = currMntPrefix + REBOOT_FILE; std::string path = currMntPrefix + REBOOT_WATCHDOG_FILE;
// Disable the reboot file mechanism parseRebootWatchdogFile(path, rebootWatchdogFile);
parseRebootFile(path, rebootFile);
if (tgtChip == xsc::CHIP_0) { if (tgtChip == xsc::CHIP_0) {
if (tgtCopy == xsc::COPY_0) { if (tgtCopy == xsc::COPY_0) {
rebootFile.img00Lock = lock; rebootWatchdogFile.img00Lock = lock;
} else { } else {
rebootFile.img01Lock = lock; rebootWatchdogFile.img01Lock = lock;
} }
} else { } else {
if (tgtCopy == xsc::COPY_0) { if (tgtCopy == xsc::COPY_0) {
rebootFile.img10Lock = lock; rebootWatchdogFile.img10Lock = lock;
} else { } else {
rebootFile.img11Lock = lock; rebootWatchdogFile.img11Lock = lock;
} }
} }
rewriteRebootFile(rebootFile); rewriteRebootWatchdogFile(rebootWatchdogFile);
} }
ReturnValue_t CoreController::backupTimeFileHandler() { ReturnValue_t CoreController::backupTimeFileHandler() {
@ -2355,10 +2442,12 @@ bool CoreController::startSdStateMachine(sd::SdCard targetActiveSd, SdCfgMode mo
} }
void CoreController::announceBootCounts() { void CoreController::announceBootCounts() {
uint64_t totalBootCount = uint64_t totalBootCount = rebootCountersFile.img00Cnt + rebootCountersFile.img01Cnt +
rebootFile.img00Cnt + rebootFile.img01Cnt + rebootFile.img10Cnt + rebootFile.img11Cnt; rebootCountersFile.img10Cnt + rebootCountersFile.img11Cnt;
uint32_t individualBootCountsP1 = (rebootFile.img00Cnt << 16) | rebootFile.img01Cnt; uint32_t individualBootCountsP1 =
uint32_t individualBootCountsP2 = (rebootFile.img10Cnt << 16) | rebootFile.img11Cnt; (rebootCountersFile.img00Cnt << 16) | rebootCountersFile.img01Cnt;
uint32_t individualBootCountsP2 =
(rebootCountersFile.img10Cnt << 16) | rebootCountersFile.img11Cnt;
triggerEvent(core::INDIVIDUAL_BOOT_COUNTS, individualBootCountsP1, individualBootCountsP2); triggerEvent(core::INDIVIDUAL_BOOT_COUNTS, individualBootCountsP1, individualBootCountsP2);
triggerEvent(core::REBOOT_COUNTER, (totalBootCount >> 32) & 0xffffffff, triggerEvent(core::REBOOT_COUNTER, (totalBootCount >> 32) & 0xffffffff,
totalBootCount & 0xffffffff); totalBootCount & 0xffffffff);
@ -2431,11 +2520,14 @@ void CoreController::announceVersionInfo() {
} }
triggerEvent(VERSION_INFO, p1, p2); triggerEvent(VERSION_INFO, p1, p2);
if (common::OBSW_VERSION_MAJOR >= 6 or common::OBSW_VERSION_MAJOR == 4) {
if (mappedSysRomAddr != nullptr) { if (mappedSysRomAddr != nullptr) {
uint32_t p1Firmware = *(reinterpret_cast<uint32_t *>(mappedSysRomAddr)); uint32_t p1Firmware = *(reinterpret_cast<uint32_t *>(mappedSysRomAddr));
uint32_t p2Firmware = *(reinterpret_cast<uint32_t *>(mappedSysRomAddr) + 1); uint32_t p2Firmware = *(reinterpret_cast<uint32_t *>(mappedSysRomAddr) + 1);
triggerEvent(FIRMWARE_INFO, p1Firmware, p2Firmware); triggerEvent(FIRMWARE_INFO, p1Firmware, p2Firmware);
} }
}
} }
void CoreController::announceCurrentImageInfo() { void CoreController::announceCurrentImageInfo() {
@ -2443,6 +2535,50 @@ void CoreController::announceCurrentImageInfo() {
triggerEvent(CURRENT_IMAGE_INFO, CURRENT_CHIP, CURRENT_COPY); 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) { bool CoreController::isNumber(const std::string &s) {
return !s.empty() && std::find_if(s.begin(), s.end(), return !s.empty() && std::find_if(s.begin(), s.end(),
[](unsigned char c) { return !std::isdigit(c); }) == s.end(); [](unsigned char c) { return !std::isdigit(c); }) == s.end();

View File

@ -24,7 +24,7 @@
class Timer; class Timer;
class SdCardManager; class SdCardManager;
struct RebootFile { struct RebootWatchdogFile {
static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10; static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10;
bool enabled = true; bool enabled = true;
@ -45,9 +45,9 @@ struct RebootFile {
xsc::Copy mechanismNextCopy = xsc::Copy::NO_COPY; xsc::Copy mechanismNextCopy = xsc::Copy::NO_COPY;
}; };
class RebootMechanismPacket : public SerialLinkedListAdapter<SerializeIF> { class RebootWatchdogPacket : public SerialLinkedListAdapter<SerializeIF> {
public: public:
RebootMechanismPacket(RebootFile& rf) { RebootWatchdogPacket(RebootWatchdogFile& rf) {
enabled = rf.enabled; enabled = rf.enabled;
maxCount = rf.maxCount; maxCount = rf.maxCount;
img00Count = rf.img00Cnt; img00Count = rf.img00Cnt;
@ -100,6 +100,38 @@ class RebootMechanismPacket : public SerialLinkedListAdapter<SerializeIF> {
SerializeElement<uint8_t> nextCopy = 0; 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 { class CoreController : public ExtendedControllerBase, public ReceivesParameterMessagesIF {
public: public:
enum ParamId : uint8_t { PREF_SD = 0, NUM_IDS }; enum ParamId : uint8_t { PREF_SD = 0, NUM_IDS };
@ -113,10 +145,15 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe
const std::string VERSION_FILE = const std::string VERSION_FILE =
"/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::VERSION_FILE_NAME); "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::VERSION_FILE_NAME);
const std::string REBOOT_FILE = const std::string LEGACY_REBOOT_WATCHDOG_FILE =
"/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::REBOOT_FILE_NAME); "/" + 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 = const std::string BACKUP_TIME_FILE =
"/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::TIME_FILE_NAME); "/" + 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_0_MOUNT_DIR[] = "/tmp/mntupdate-xdi-qspi0-nom-rootfs";
static constexpr char CHIP_0_COPY_1_MOUNT_DIR[] = "/tmp/mntupdate-xdi-qspi0-gold-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{}; std::array<uint8_t, 1024> dirListingBuf{};
DirListingDumpContext dumpContext{}; DirListingDumpContext dumpContext{};
RebootFile rebootFile = {}; RebootWatchdogFile rebootWatchdogFile = {};
RebootCountersFile rebootCountersFile = {};
CommandExecutor cmdExecutor; CommandExecutor cmdExecutor;
SimpleRingBuffer cmdReplyBuf; SimpleRingBuffer cmdReplyBuf;
@ -318,12 +356,14 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe
void currentStateSetter(sd::SdCard sdCard, sd::SdState newState); void currentStateSetter(sd::SdCard sdCard, sd::SdState newState);
void executeNextExternalSdCommand(); void executeNextExternalSdCommand();
void checkExternalSdCommandStatus(); void checkExternalSdCommandStatus();
void performRebootFileHandling(bool recreateFile); void performRebootWatchdogHandling(bool recreateFile);
void performRebootCountersHandling(bool recreateFile);
ReturnValue_t actionListDirectoryIntoFile(ActionId_t actionId, MessageQueueId_t commandedBy, ReturnValue_t actionListDirectoryIntoFile(ActionId_t actionId, MessageQueueId_t commandedBy,
const uint8_t* data, size_t size); const uint8_t* data, size_t size);
ReturnValue_t actionListDirectoryDumpDirectly(ActionId_t actionId, MessageQueueId_t commandedBy, ReturnValue_t actionListDirectoryDumpDirectly(ActionId_t actionId, MessageQueueId_t commandedBy,
const uint8_t* data, size_t size); 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, ReturnValue_t actionListDirectoryCommonCommandCreator(const uint8_t* data, size_t size,
std::ostringstream& oss); std::ostringstream& oss);
@ -337,12 +377,14 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe
int handleBootCopyProtAtIndex(xsc::Chip targetChip, xsc::Copy targetCopy, bool protect, int handleBootCopyProtAtIndex(xsc::Chip targetChip, xsc::Copy targetCopy, bool protect,
bool& protOperationPerformed, bool selfChip, bool selfCopy, bool& protOperationPerformed, bool selfChip, bool selfCopy,
bool allChips, bool allCopies, uint8_t arrIdx); 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); 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); void setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::Copy tgtCopy);
bool parseRebootFile(std::string path, RebootFile& file); bool parseRebootWatchdogFile(std::string path, RebootWatchdogFile& file);
void rewriteRebootFile(RebootFile file); bool parseRebootCountersFile(std::string path, RebootCountersFile& file);
void rewriteRebootWatchdogFile(RebootWatchdogFile file);
void rewriteRebootCountersFile(RebootCountersFile file);
void announceBootCounts(); void announceBootCounts();
void announceVersionInfo(); void announceVersionInfo();
void announceCurrentImageInfo(); 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 // GPIO definitions of signals connected to the virtual channel interfaces of the PTME IP Core
GpioCookie* gpioCookiePtmeIp = new GpioCookie; GpioCookie* gpioCookiePtmeIp = new GpioCookie;
GpiodRegularByLineName* gpio = nullptr; 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"); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC0, "PAPB VC0");
gpioCookiePtmeIp->addGpio(gpioIds::VC0_PAPB_EMPTY, gpio); 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"); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC1, "PAPB VC1");
gpioCookiePtmeIp->addGpio(gpioIds::VC1_PAPB_EMPTY, gpio); 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"); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC2, "PAPB VC2");
gpioCookiePtmeIp->addGpio(gpioIds::VC2_PAPB_EMPTY, gpio); 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"); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC3, "PAPB VC3");
gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_EMPTY, gpio); gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_EMPTY, gpio);
gpio = new GpiodRegularByLineName(q7s::gpioNames::PTME_RESETN, "PTME RESETN", 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"); gpioChecker(args.gpioComIF.addGpios(gpioCookiePtmeIp), "PTME PAPB VCs");
// Creating virtual channel interfaces // 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); 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); 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); 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); q7s::UIO_PTME, q7s::uiomapids::PTME_VC3);
// Creating ptme object and adding virtual channel interfaces // Creating ptme object and adding virtual channel interfaces
Ptme* ptme = new Ptme(objects::PTME); Ptme* ptme = new Ptme(objects::PTME);
ptme->addVcInterface(ccsds::VC0, vc0); ptme->addVcInterface(ccsds::VC0, vc0);
@ -1017,6 +1030,7 @@ void ObjectFactory::createRadSensorChipSelect(LinuxLibgpioIF* gpioIF) {
void ObjectFactory::createPlI2cResetGpio(LinuxLibgpioIF* gpioIF) { void ObjectFactory::createPlI2cResetGpio(LinuxLibgpioIF* gpioIF) {
using namespace gpio; using namespace gpio;
if (common::OBSW_VERSION_MAJOR >= 6 or common::OBSW_VERSION_MAJOR == 4) {
if (gpioIF == nullptr) { if (gpioIF == nullptr) {
return; return;
} }
@ -1029,4 +1043,5 @@ void ObjectFactory::createPlI2cResetGpio(LinuxLibgpioIF* gpioIF) {
gpioIF->pullLow(gpioIds::PL_I2C_ARESETN); gpioIF->pullLow(gpioIds::PL_I2C_ARESETN);
TaskFactory::delayTask(1); TaskFactory::delayTask(1);
gpioIF->pullHigh(gpioIds::PL_I2C_ARESETN); gpioIF->pullHigh(gpioIds::PL_I2C_ARESETN);
}
} }

View File

@ -324,6 +324,10 @@ void scheduling::initTasks() {
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER); 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 #if OBSW_ADD_SYRLINKS == 1
PeriodicTaskIF* syrlinksCom = factory->createPeriodicTask( PeriodicTaskIF* syrlinksCom = factory->createPeriodicTask(

View File

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

View File

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

View File

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

View File

@ -96,6 +96,25 @@ ReturnValue_t TemperatureSensorInserter::performOperation(uint8_t opCode) {
} }
break; 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): { case (TestCase::COLD_CAMERA): {
if (cycles == 15) { if (cycles == 15) {
sif::debug << "Setting cold CAM temperature" << std::endl; 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 = 4,
COLD_STR_CONSECUTIVE = 5, COLD_STR_CONSECUTIVE = 5,
COLD_CAMERA = 6, COLD_CAMERA = 6,
COLD_PLOC_CONSECUTIVE = 7,
}; };
int iteration = 0; int iteration = 0;
uint32_t cycles = 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" #include "fsfw/serviceinterface/ServiceInterface.h"
PapbVcInterface::PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbEmptyId, PapbVcInterface::PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbBusyId,
std::string uioFile, int mapNum) gpioId_t papbEmptyId, std::string uioFile, int mapNum)
: gpioComIF(gpioComIF), papbEmptyId(papbEmptyId), uioFile(std::move(uioFile)), mapNum(mapNum) {} : gpioComIF(gpioComIF),
papbBusyId(papbBusyId),
papbEmptyId(papbEmptyId),
uioFile(std::move(uioFile)),
mapNum(mapNum) {}
PapbVcInterface::~PapbVcInterface() {} PapbVcInterface::~PapbVcInterface() {}
ReturnValue_t PapbVcInterface::initialize() { ReturnValue_t PapbVcInterface::initialize() {
UioMapper uioMapper(uioFile, mapNum); UioMapper uioMapper(uioFile, mapNum);
ReturnValue_t result = uioMapper.getMappedAdress(const_cast<uint32_t**>(&vcBaseReg), ReturnValue_t result = uioMapper.getMappedAdress(const_cast<uint32_t**>(&vcBaseReg),
UioMapper::Permissions::READ_WRITE); UioMapper::Permissions::WRITE_ONLY);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
return result; return result;
} }
@ -28,16 +32,63 @@ ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size) {
if (size < 4) { if (size < 4) {
return returnvalue::FAILED; return returnvalue::FAILED;
} }
if (pollReadyForPacket()) { if (pollInterfaceReadiness(0, true) == returnvalue::OK) {
startPacketTransfer(ByteWidthCfg::ONE); startPacketTransfer(ByteWidthCfg::ONE);
} else { } else {
return DirectTmSinkIF::IS_BUSY; 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++) { 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]); *(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(); completePacketTransfer();
} else {
abortPacketTransfer();
return returnvalue::FAILED;
}
return returnvalue::OK; return returnvalue::OK;
} }
@ -47,33 +98,60 @@ void PapbVcInterface::startPacketTransfer(ByteWidthCfg initWidth) {
void PapbVcInterface::completePacketTransfer() { *vcBaseReg = CONFIG_END; } 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. // 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. // Bit 5, see PTME ptme_001_01-0-7-r2 Table 31.
uint32_t reg = *vcBaseReg; uint32_t reg = *vcBaseReg;
// bool busy = (reg >> 5) & 0b1; bool busy = (reg >> 5) & 0b1;
return (reg >> 6) & 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; ReturnValue_t result = returnvalue::OK;
gpio::Levels papbEmptyState = gpio::Levels::HIGH; gpio::Levels papbEmptyState = gpio::Levels::HIGH;
result = gpioComIF->readGpio(papbEmptyId, papbEmptyState); result = gpioComIF->readGpio(papbEmptyId, papbEmptyState);
if (result != returnvalue::OK) { 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; << std::endl;
return true; return;
} }
if (papbEmptyState == gpio::Levels::HIGH) { 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(); } 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 uioFile UIO file providing access to the PAPB bus
* @param mapNum Map number of UIO map associated with this virtual channel * @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(); virtual ~PapbVcInterface();
bool isBusy() const override; bool isBusy() const override;
@ -82,6 +83,9 @@ class PapbVcInterface : public VirtualChannelIF {
static constexpr long int MAX_DELAY_PAPB_POLLING_NS = 40; static constexpr long int MAX_DELAY_PAPB_POLLING_NS = 40;
LinuxLibgpioIF* gpioComIF = nullptr; 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 */ /** High when external buffer memory of virtual channel is empty */
gpioId_t papbEmptyId = gpio::NO_GPIO; 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. * @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 * @brief This function can be used for debugging to check whether there are packets in
* the packet buffer of the virtual channel or not. * 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) * @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) { if (not someonesBusy) {
TaskFactory::delayTask(100); TaskFactory::delayTask(100);
} else if (vcBusyDuringDump) { } else if (vcBusyDuringDump) {
// TODO: Might not be necessary
sif::debug << "VC busy, delaying" << std::endl;
TaskFactory::delayTask(10); 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) { if (not busy) {
TaskFactory::delayTask(100); TaskFactory::delayTask(100);
} else if (dumpContext.vcBusyDuringDump) { } else if (dumpContext.vcBusyDuringDump) {
sif::debug << "VC busy, delaying" << std::endl;
// TODO: Might not be necessary
TaskFactory::delayTask(10); 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, CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan,
StorageManagerIF** ipcStore, StorageManagerIF** tmStore, StorageManagerIF** ipcStore, StorageManagerIF** tmStore,
PersistentTmStores& stores, PersistentTmStores& stores,
uint32_t eventManagerQueueDepth) { uint32_t eventManagerQueueDepth, bool enableHkSets) {
// Framework objects // Framework objects
new EventManager(objects::EVENT_MANAGER, eventManagerQueueDepth); new EventManager(objects::EVENT_MANAGER, eventManagerQueueDepth);
auto healthTable = new HealthTable(objects::HEALTH_TABLE); auto healthTable = new HealthTable(objects::HEALTH_TABLE);
if (healthTable_ != nullptr) { if (healthTable_ != nullptr) {
*healthTable_ = healthTable; *healthTable_ = healthTable;
} }
new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER, 5, enableHkSets, 120);
new VerificationReporter(); new VerificationReporter();
auto* timeStamper = new CdsShortTimeStamper(objects::TIME_STAMPER); auto* timeStamper = new CdsShortTimeStamper(objects::TIME_STAMPER);
StorageManagerIF* tcStore; StorageManagerIF* tcStore;

View File

@ -45,7 +45,8 @@ namespace ObjectFactory {
void produceGenericObjects(HealthTableIF** healthTable, PusTmFunnel** pusFunnel, void produceGenericObjects(HealthTableIF** healthTable, PusTmFunnel** pusFunnel,
CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan, CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan,
StorageManagerIF** ipcStore, StorageManagerIF** tmStore, StorageManagerIF** ipcStore, StorageManagerIF** tmStore,
PersistentTmStores& stores, uint32_t eventManagerQueueDepth); PersistentTmStores& stores, uint32_t eventManagerQueueDepth,
bool enableHkSets);
void createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF& pwrSwitcher, void createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF& pwrSwitcher,
HeaterHandler*& heaterHandler); 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 CONF_FOLDER[] = "conf";
static constexpr char VERSION_FILE_NAME[] = "version.txt"; 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 char TIME_FILE_NAME[] = "time_backup.txt";
static constexpr uint32_t SYS_ROM_BASE_ADDR = 0x80000000; 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) { if (mainLineSwitcher->getSwitchState(mainLineSwitch) == SWITCH_OFF) {
waitForSwitchOff = false; waitForSwitchOff = false;
mode = MODE_OFF; mode = MODE_OFF;
busyWithSwitchCommanding = false;
modeHelper.modeChanged(mode, submode); modeHelper.modeChanged(mode, submode);
} }
} }
if (busyWithSwitchCommanding and heaterCmdBusyCd.hasTimedOut()) {
busyWithSwitchCommanding = false;
}
} catch (const std::out_of_range& e) { } catch (const std::out_of_range& e) {
sif::warning << "HeaterHandler::performOperation: " sif::warning << "HeaterHandler::performOperation: "
"Out of range error | " "Out of range error | "
@ -101,23 +105,23 @@ ReturnValue_t HeaterHandler::initializeHeaterMap() {
void HeaterHandler::readCommandQueue() { void HeaterHandler::readCommandQueue() {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
CommandMessage command; CommandMessage command;
do { if (not busyWithSwitchCommanding) {
result = commandQueue->receiveMessage(&command); result = commandQueue->receiveMessage(&command);
if (result == MessageQueueIF::EMPTY) { if (result == MessageQueueIF::EMPTY) {
break; return;
} else if (result != returnvalue::OK) { } else if (result != returnvalue::OK) {
sif::warning << "HeaterHandler::readCommandQueue: Message reception error" << std::endl; sif::warning << "HeaterHandler::readCommandQueue: Message reception error" << std::endl;
break; return;
}
result = actionHelper.handleActionMessage(&command);
if (result == returnvalue::OK) {
continue;
} }
result = modeHelper.handleModeCommand(&command); result = modeHelper.handleModeCommand(&command);
if (result == returnvalue::OK) { 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, 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.action = action;
heater.cmdActive = true; heater.cmdActive = true;
heater.replyQueue = commandedBy; heater.replyQueue = commandedBy;
busyWithSwitchCommanding = true;
heaterCmdBusyCd.resetTimer();
return returnvalue::OK; return returnvalue::OK;
} }
@ -249,6 +255,7 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) {
sif::error << "HeaterHandler::handleSwitchOnCommand: Main switch setting on timeout" sif::error << "HeaterHandler::handleSwitchOnCommand: Main switch setting on timeout"
<< std::endl; << std::endl;
heater.cmdActive = false; heater.cmdActive = false;
busyWithSwitchCommanding = false;
heater.waitMainSwitchOn = false; heater.waitMainSwitchOn = false;
if (heater.replyQueue != commandQueue->getId()) { if (heater.replyQueue != commandQueue->getId()) {
actionHelper.finish(false, heater.replyQueue, heater.action, MAIN_SWITCH_SET_TIMEOUT); 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 // Check state of main line switch
ReturnValue_t mainSwitchState = mainLineSwitcher->getSwitchState(mainLineSwitch); ReturnValue_t mainSwitchState = mainLineSwitcher->getSwitchState(mainLineSwitch);
if (mainSwitchState == PowerSwitchIF::SWITCH_ON) { if (mainSwitchState == PowerSwitchIF::SWITCH_ON) {
if (getSwitchState(heaterIdx) == SwitchState::OFF) {
gpioId_t gpioId = heater.gpioId; gpioId_t gpioId = heater.gpioId;
result = gpioInterface->pullHigh(gpioId); result = gpioInterface->pullHigh(gpioId);
if (result != returnvalue::OK) { 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; << " high" << std::endl;
triggerEvent(GPIO_PULL_HIGH_FAILED, result); triggerEvent(GPIO_PULL_HIGH_FAILED, result);
} else { }
if (result == returnvalue::OK) {
triggerEvent(HEATER_WENT_ON, heaterIdx, 0); 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); MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
heater.switchState = ON; heater.switchState = ON;
} }
} EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO,
} else { MODE_ON, 0);
triggerEvent(SWITCH_ALREADY_ON, heaterIdx); busyWithSwitchCommanding = false;
}
mode = HasModesIF::MODE_ON; mode = HasModesIF::MODE_ON;
modeHelper.modeChanged(mode, submode); modeHelper.modeChanged(mode, submode);
}
// There is no need to send action finish replies if the sender was the // There is no need to send action finish replies if the sender was the
// HeaterHandler itself // HeaterHandler itself
if (heater.replyQueue != commandQueue->getId()) { if (heater.replyQueue != commandQueue->getId()) {
@ -312,30 +317,33 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) {
void HeaterHandler::handleSwitchOffCommand(heater::Switch heaterIdx) { void HeaterHandler::handleSwitchOffCommand(heater::Switch heaterIdx) {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
auto& heater = heaterVec.at(heaterIdx); auto& heater = heaterVec.at(heaterIdx);
// Check whether switch is already off
if (getSwitchState(heaterIdx)) {
gpioId_t gpioId = heater.gpioId; gpioId_t gpioId = heater.gpioId;
result = gpioInterface->pullLow(gpioId); result = gpioInterface->pullLow(gpioId);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
sif::error << "HeaterHandler::handleSwitchOffCommand: Failed to pull gpio with id" << gpioId sif::error << "HeaterHandler::handleSwitchOffCommand: Failed to pull gpio with id" << gpioId
<< " low" << std::endl; << " low" << std::endl;
triggerEvent(GPIO_PULL_LOW_FAILED, result); 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); MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
heater.switchState = OFF; heater.switchState = OFF;
} }
triggerEvent(HEATER_WENT_OFF, heaterIdx, 0); triggerEvent(HEATER_WENT_OFF, heaterIdx, 0);
} else {
triggerEvent(SWITCH_ALREADY_OFF, heaterIdx);
}
EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO, EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO,
MODE_OFF, 0); MODE_OFF, 0);
// When all switches are off, also main line switch will be turned off // When all switches are off, also main line switch will be turned off
if (allSwitchesOff()) { if (allSwitchesOff()) {
mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF); mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF);
waitForSwitchOff = true; waitForSwitchOff = true;
}
}
} else { } else {
triggerEvent(SWITCH_ALREADY_OFF, heaterIdx); busyWithSwitchCommanding = false;
}
} }
if (heater.replyQueue != NO_COMMANDER) { if (heater.replyQueue != NO_COMMANDER) {
// Report back switch command reply if necessary // Report back switch command reply if necessary

View File

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

View File

@ -17,6 +17,8 @@
using namespace returnvalue; using namespace returnvalue;
static constexpr bool DEBUG_DUMPS = false;
PersistentTmStore::PersistentTmStore(PersistentTmStoreArgs args) PersistentTmStore::PersistentTmStore(PersistentTmStoreArgs args)
: SystemObject(args.objectId), : SystemObject(args.objectId),
tmStore(args.tmStore), tmStore(args.tmStore),
@ -32,6 +34,91 @@ ReturnValue_t PersistentTmStore::cancelDump() {
return returnvalue::OK; 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() { ReturnValue_t PersistentTmStore::assignAndOrCreateMostRecentFile() {
if (not activeFile.has_value()) { if (not activeFile.has_value()) {
return createMostRecentFile(std::nullopt); return createMostRecentFile(std::nullopt);
@ -159,6 +246,12 @@ bool PersistentTmStore::updateBaseDir() {
if (not exists(basePath, e)) { if (not exists(basePath, e)) {
create_directories(basePath); 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; baseDirUninitialized = false;
return true; return true;
} }
@ -189,12 +282,20 @@ ReturnValue_t PersistentTmStore::startDumpFromUpTo(uint32_t fromUnixSeconds,
if (state == State::DUMPING) { if (state == State::DUMPING) {
return returnvalue::FAILED; return returnvalue::FAILED;
} }
dumpParams.dirIter = directory_iterator(basePath); auto dirIter = directory_iterator(basePath);
if (dumpParams.dirIter == directory_iterator()) { // Directory empty case.
if (dirIter == directory_iterator()) {
return returnvalue::FAILED; return returnvalue::FAILED;
} }
dumpParams.fromUnixTime = fromUnixSeconds; dumpParams.fromUnixTime = fromUnixSeconds;
dumpParams.untilUnixTime = upToUnixSeconds; 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; state = State::DUMPING;
return loadNextDumpFile(); return loadNextDumpFile();
} }
@ -203,50 +304,55 @@ ReturnValue_t PersistentTmStore::loadNextDumpFile() {
using namespace std::filesystem; using namespace std::filesystem;
dumpParams.currentSize = 0; dumpParams.currentSize = 0;
std::error_code e; std::error_code e;
for (; dumpParams.dirIter != directory_iterator(); dumpParams.dirIter++) { // Handle iteration, which does not necessarily have to increment the set iterator
dumpParams.dirEntry = *dumpParams.dirIter; // if there are multiple files for a given timestamp.
if (dumpParams.dirEntry.is_directory(e)) { auto handleIteration = [&](DumpIndex& dumpIndex) {
continue; 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) { 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; continue;
} }
// sif::debug << "Path: " << dumpParams.dirEntry.path() << std::endl; std::ifstream ifile(dumpParams.currentFile, std::ios::binary);
// 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);
if (ifile.bad()) { 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; continue;
} }
ifile.read(reinterpret_cast<char*>(fileBuf.data()), ifile.read(reinterpret_cast<char*>(fileBuf.data()),
static_cast<std::streamsize>(dumpParams.fileSize)); static_cast<std::streamsize>(dumpParams.fileSize));
// Increment iterator for next cycle. // Next file is loaded, exit the loop.
dumpParams.dirIter++; handleIteration(dumpIndex);
return returnvalue::OK; return returnvalue::OK;
} }
}
// Directory iterator was consumed and we are done. // Directory iterator was consumed and we are done.
state = State::IDLE; state = State::IDLE;
return DUMP_DONE; 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 // Delete the file and load next. Could use better algorithm to partially
// restore the file dump, but for now do not trust the file. // restore the file dump, but for now do not trust the file.
std::error_code e; std::error_code e;
std::filesystem::remove(dumpParams.dirEntry.path().c_str(), e); std::filesystem::remove(dumpParams.currentFile.c_str(), e);
if (dumpParams.dirEntry.path() == activeFile) { if (dumpParams.currentFile == activeFile) {
activeFile == std::nullopt; activeFile == std::nullopt;
assignAndOrCreateMostRecentFile(); assignAndOrCreateMostRecentFile();
} }
@ -302,37 +408,9 @@ ReturnValue_t PersistentTmStore::pathToTime(const std::filesystem::path& path, s
ReturnValue_t PersistentTmStore::createMostRecentFile(std::optional<uint8_t> suffix) { ReturnValue_t PersistentTmStore::createMostRecentFile(std::optional<uint8_t> suffix) {
using namespace std::filesystem; using namespace std::filesystem;
unsigned currentIdx = 0; size_t currentIdx;
path pathStart = basePath / baseName; createFileName(currentTv, suffix, currentIdx);
memcpy(fileBuf.data() + currentIdx, pathStart.c_str(), pathStart.string().length()); path newPath(std::string(reinterpret_cast<const char*>(filePathBuf.data()), currentIdx));
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));
std::ofstream of(newPath, std::ios::binary); std::ofstream of(newPath, std::ios::binary);
activeFile = newPath; activeFile = newPath;
activeFileTv = currentTv; activeFileTv = currentTv;
@ -354,3 +432,33 @@ void PersistentTmStore::getStartAndEndTimeCurrentOrLastDump(uint32_t& startTime,
startTime = dumpParams.fromUnixTime; startTime = dumpParams.fromUnixTime;
endTime = dumpParams.untilUnixTime; 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 <mission/memory/SdCardMountedIF.h>
#include <filesystem> #include <filesystem>
#include <set>
#include "eive/eventSubsystemIds.h" #include "eive/eventSubsystemIds.h"
#include "eive/resultClassIds.h" #include "eive/resultClassIds.h"
@ -37,6 +38,14 @@ struct PersistentTmStoreArgs {
SdCardMountedIF& sdcMan; 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 { class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
public: public:
enum class State { IDLE, DUMPING }; enum class State { IDLE, DUMPING };
@ -96,7 +105,10 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
std::string baseName; std::string baseName;
uint8_t currentSameSecNumber = 0; uint8_t currentSameSecNumber = 0;
std::filesystem::path basePath; std::filesystem::path basePath;
// std::filesystem::path pathStart = basePath / baseName;
uint32_t rolloverDiffSeconds = 0; uint32_t rolloverDiffSeconds = 0;
std::array<uint8_t, 524> filePathBuf{};
size_t basePathSize;
std::array<uint8_t, MAX_FILESIZE> fileBuf{}; std::array<uint8_t, MAX_FILESIZE> fileBuf{};
timeval currentTv; timeval currentTv;
timeval activeFileTv{}; timeval activeFileTv{};
@ -106,8 +118,10 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
uint32_t fromUnixTime = 0; uint32_t fromUnixTime = 0;
uint32_t untilUnixTime = 0; uint32_t untilUnixTime = 0;
uint32_t currentFileUnixStamp = 0; uint32_t currentFileUnixStamp = 0;
std::filesystem::directory_iterator dirIter; std::filesystem::path currentFile;
std::filesystem::directory_entry dirEntry; std::set<DumpIndex> orderedDumpFilestamps{};
std::set<DumpIndex>::iterator dumpIter;
std::optional<uint8_t> currentSameFileIdx = 0;
size_t fileSize = 0; size_t fileSize = 0;
size_t currentSize = 0; size_t currentSize = 0;
}; };
@ -122,10 +136,13 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject {
[[nodiscard]] MessageQueueId_t getCommandQueue() const override; [[nodiscard]] MessageQueueId_t getCommandQueue() const override;
void calcDiffSeconds(RolloverInterval intervalUnit, uint32_t intervalCount); void calcDiffSeconds(RolloverInterval intervalUnit, uint32_t intervalCount);
ReturnValue_t buildDumpSet(uint32_t fromUnixSeconds, uint32_t upToUnixSeconds);
ReturnValue_t createMostRecentFile(std::optional<uint8_t> suffix); ReturnValue_t createMostRecentFile(std::optional<uint8_t> suffix);
static ReturnValue_t pathToTime(const std::filesystem::path& path, struct tm& time); static ReturnValue_t pathToTime(const std::filesystem::path& path, struct tm& time);
void fileToPackets(const std::filesystem::path& path, uint32_t unixStamp); void fileToPackets(const std::filesystem::path& path, uint32_t unixStamp);
ReturnValue_t loadNextDumpFile(); 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(); bool updateBaseDir();
ReturnValue_t assignAndOrCreateMostRecentFile(); 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 target_sources(${UNITTEST_NAME} PRIVATE
main.cpp main.cpp
testEnvironment.cpp testEnvironment.cpp
testStampInFilename.cpp testGenericFilesystem.cpp
hdlcEncodingRw.cpp hdlcEncodingRw.cpp
printChar.cpp printChar.cpp
) )

View File

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

View File

@ -27,7 +27,7 @@ void factory(void* args) {
new HouseKeepingMock(); new HouseKeepingMock();
eventManager = new EventManagerMock(); eventManager = new EventManagerMock();
new HealthTable(objects::HEALTH_TABLE); 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); 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);
}