Compare commits

..

39 Commits

Author SHA1 Message Date
90ca65f9a9 Merge pull request 'prep v1.44.1' (#591) from prep_v1.44.1 into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #591
2023-04-12 13:26:37 +02:00
f4c156479a prep v1.44.1
Some checks are pending
EIVE/eive-obsw/pipeline/head Build queued...
2023-04-12 13:26:23 +02:00
67e94e1ee7 Merge pull request 'Move CFDP handler to mission code' (#586) from move_cfdp_handler into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #586
Reviewed-by: Michael Steinert <steinertm@irs.uni-stuttgart.de>
2023-04-12 12:59:09 +02:00
b20e38a2bc Merge branch 'develop' into move_cfdp_handler
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build queued...
2023-04-12 12:59:00 +02:00
d71031f2e4 Merge pull request 'Shared Power Switch Assembly' (#590) from assy_proper_recovery_handling into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #590
2023-04-12 12:58:48 +02:00
df7236cfee fix untitest
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-04-12 00:25:21 +02:00
c9cc1d4cfe done
Some checks failed
EIVE/eive-obsw/pipeline/head Build started...
EIVE/eive-obsw/pipeline/pr-develop There was a failure building this commit
2023-04-11 22:58:13 +02:00
2bb0f530fe Merge pull request 'ACS Board: Better reply result handling' (#588) from possible_fixes_acs_brd_polling into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #588
Reviewed-by: Marius Eggert <eggertm@irs.uni-stuttgart.de>
2023-04-11 18:20:51 +02:00
b3f5e74609 typo
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build queued...
2023-04-11 18:18:33 +02:00
ff28b628a4 leave that value
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build queued...
2023-04-11 18:17:30 +02:00
5925de94e7 changelog
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build started...
2023-04-11 18:16:58 +02:00
7c36660000 that was nasty
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build started...
2023-04-11 18:15:12 +02:00
776a53b243 dont send anything in break CD 2023-04-11 18:03:07 +02:00
d42b6798e0 better reply result handling
Some checks are pending
EIVE/eive-obsw/pipeline/head Build queued...
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-04-11 17:29:32 +02:00
fa94c67e99 Merge pull request 'add a way to detect off or broken SUS devices' (#587) from bugfix_sus_fdir into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #587
Reviewed-by: Marius Eggert <eggertm@irs.uni-stuttgart.de>
2023-04-11 17:24:01 +02:00
4e9a074e82 changelog
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build started...
2023-04-11 17:19:16 +02:00
b82f19ea50 reset reply result on mode change
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build started...
2023-04-11 17:18:12 +02:00
271830422a add a way to detect off devices
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-04-11 17:12:31 +02:00
7869289abc bump fsfw
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-04-11 16:53:07 +02:00
0a84eab0ef move cfdp handler
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-04-11 16:51:49 +02:00
b816b386cf bump fsfw
Some checks failed
EIVE/eive-obsw/pipeline/head There was a failure building this commit
2023-04-11 16:47:56 +02:00
d552b51c0d Merge pull request 'Custom OBSW update path' (#582) from feature_custom_obsw_update_path into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #582
2023-04-11 16:12:42 +02:00
52620f3dda Merge branch 'develop' into feature_custom_obsw_update_path
Some checks are pending
EIVE/eive-obsw/pipeline/pr-develop Build queued...
2023-04-11 16:12:28 +02:00
d7eaceb0fc Merge pull request 'EM bugfix' (#585) from bugfix_em into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #585
2023-04-11 16:11:25 +02:00
e01fe19d53 EM bugfix
Some checks are pending
EIVE/eive-obsw/pipeline/head This commit looks good
EIVE/eive-obsw/pipeline/pr-develop Build queued...
2023-04-11 15:34:46 +02:00
af1d0759e1 remove re-declaration
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-04-11 15:26:33 +02:00
af9f346698 use exceptionless API
Some checks failed
EIVE/eive-obsw/pipeline/pr-develop There was a failure building this commit
2023-04-11 15:23:44 +02:00
abef9da9f9 Merge pull request 'Feature and Bugfixes: Better SDC state machine handling' (#584) from feature_sdc_fsm_mark_sd_unusbale into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #584
Reviewed-by: Marius Eggert <eggertm@irs.uni-stuttgart.de>
2023-04-11 09:52:16 +02:00
4afddad503 typo
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-04-08 17:48:45 +02:00
cc39acd436 changelog
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
2023-04-08 17:47:56 +02:00
2df556c5be some bugfixes, seems to work now
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
2023-04-08 17:46:54 +02:00
aa43912279 everything except SDC switch looking good.. 2023-04-08 17:13:42 +02:00
1c0fbace4d Merge remote-tracking branch 'origin/develop' into feature_custom_obsw_update_path
All checks were successful
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-04-08 15:56:08 +02:00
2fb7ac7b4b Merge pull request 'Bugfix SD state machine' (#583) from bugfix_sdc_state_machine into develop
All checks were successful
EIVE/eive-obsw/pipeline/head This commit looks good
Reviewed-on: #583
2023-04-08 15:55:03 +02:00
c89e332843 changelog
Some checks are pending
EIVE/eive-obsw/pipeline/head This commit looks good
EIVE/eive-obsw/pipeline/pr-develop Build started...
2023-04-08 13:25:31 +02:00
10f552f56a bugfix and simplifications for SDC state machine 2023-04-08 13:24:21 +02:00
310f8f5f3c SDC state machine printouts 2023-04-08 11:53:44 +02:00
584b6e3038 changelog and tmtc bump
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
EIVE/eive-obsw/pipeline/pr-develop This commit looks good
2023-04-08 11:40:23 +02:00
a81b24b67f allow custom filename
Some checks are pending
EIVE/eive-obsw/pipeline/head Build started...
2023-04-08 11:31:31 +02:00
34 changed files with 662 additions and 266 deletions

View File

@ -16,6 +16,39 @@ will consitute of a breaking change warranting a new major release:
# [unreleased]
# [v1.44.1] 2023-04-12
- eive-tmtc: v2.22.1
## Fixed
- Bugfixes and improvements for SDC state machine. Internal state was not updated correctly due
to a regression, so commanding the SDC state machine externally lead to confusing results.
- Fixed a bug in persistent TM store, where the active file was not reset of SD card switches.
SD card switch from 0 to 1 and vice-versa works without errors from persistent TM stores now.
- Add a way for the SUS polling to detect broken or off devices by checking the retrieved
temperature for the all-ones value (0x0fff).
- Better reply result handling for the ACS board devices.
- ADIS1650X initial timeout handling now performed in device handler.
- The RW assembly and TCS board assembly now perform proper power switch handling for their
recovery handling.
## Changed
- Added additional logic for SDC state machine so that the SD cards are marked unusable when
the active SD card is switched or there is a transition from hot redundant to cold redundant mode.
This gives other tasks some time to register the SD cards being unusable, and therefore provides
a way for them to perform any re-initialization tasks necessary after SD card switches.
- The TCS controller pauses operations related to the TCS board assembly (reading sensors and
the primary control loop) while a TCS board recovery is on-going.
## Changed
- Allow specifying custom OBSW update filename. This allowed keeping a cleaner file structure
where each update has a name including the version
- The files extracted during an update process are deleted after the update was performed to keep
the update directory cleaner.
# [v1.44.0] 2023-04-07
- eive-tmtc: v2.22.0

View File

@ -11,7 +11,7 @@ cmake_minimum_required(VERSION 3.13)
set(OBSW_VERSION_MAJOR 1)
set(OBSW_VERSION_MINOR 44)
set(OBSW_VERSION_REVISION 0)
set(OBSW_VERSION_REVISION 1)
# set(CMAKE_VERBOSE TRUE)

View File

@ -54,7 +54,6 @@ CoreController::CoreController(object_id_t objectId, bool enableHkSet)
// Set up state of SD card manager and own initial state.
// Stopwatch watch;
sdcMan->updateSdCardStateFile();
sdcMan->updateSdStatePair();
SdCardManager::SdStatePair sdStates;
sdcMan->getSdCardsStatus(sdStates);
auto sdCard = sdcMan->getPreferredSdCard();
@ -368,7 +367,7 @@ ReturnValue_t CoreController::initSdCardBlocking() {
}
if (sdInfo.cfgMode == SdCfgMode::COLD_REDUNDANT) {
updateSdInfoOther();
updateInternalSdInfo();
sif::info << "Cold redundant SD card configuration, preferred SD card: "
<< static_cast<int>(sdInfo.active) << std::endl;
result = sdColdRedundantBlockingInit();
@ -404,23 +403,23 @@ ReturnValue_t CoreController::sdStateMachine() {
} else {
// Still update SD state file
if (sdInfo.cfgMode == SdCfgMode::PASSIVE) {
sdFsmState = SdStates::UPDATE_INFO;
sdFsmState = SdStates::UPDATE_SD_INFO_END;
} else {
sdInfo.cycleCount = 0;
sdInfo.commandExecuted = false;
sdFsmState = SdStates::GET_INFO;
sdInfo.commandPending = false;
sdFsmState = SdStates::UPDATE_SD_INFO_START;
}
}
}
// This lambda checks the non-blocking operation and assigns the new state on success.
// It returns true for an operation success and false otherwise
auto nonBlockingOpChecking = [&](SdStates newStateOnSuccess, uint16_t maxCycleCount,
// This lambda checks the non-blocking operation of the SD card manager and assigns the new
// state on success. It returns true for an operation success and false otherwise
auto nonBlockingSdcOpChecking = [&](SdStates newStateOnSuccess, uint16_t maxCycleCount,
std::string opPrintout) {
SdCardManager::OpStatus status = sdcMan->checkCurrentOp(operation);
if (status == SdCardManager::OpStatus::SUCCESS) {
sdFsmState = newStateOnSuccess;
sdInfo.commandExecuted = false;
sdInfo.commandPending = false;
sdInfo.cycleCount = 0;
return true;
} else if (sdInfo.cycleCount > 4) {
@ -431,26 +430,29 @@ ReturnValue_t CoreController::sdStateMachine() {
return false;
};
if (sdFsmState == SdStates::GET_INFO) {
if (not sdInfo.commandExecuted) {
if (sdFsmState == SdStates::UPDATE_SD_INFO_START) {
if (not sdInfo.commandPending) {
// Create updated status file
result = sdcMan->updateSdCardStateFile();
if (result != returnvalue::OK) {
sif::warning << "CoreController::sdStateMachine: Updating SD card state file failed"
<< std::endl;
}
sdFsmState = SdStates::SET_STATE_SELF;
sdInfo.commandExecuted = false;
sdInfo.cycleCount = 0;
} else {
nonBlockingOpChecking(SdStates::SET_STATE_SELF, 4, "Updating SDC file");
}
}
if (sdFsmState == SdStates::SET_STATE_SELF) {
if (not sdInfo.commandExecuted) {
result = sdcMan->getSdCardsStatus(sdInfo.currentState);
updateSdInfoOther();
updateInternalSdInfo();
auto currentlyActiveSdc = sdcMan->getActiveSdCard();
// Used/active SD card switches, so mark SD card unusable so other tasks have some time
// registering the unavailable SD card.
if (not currentlyActiveSdc.has_value() or
((currentlyActiveSdc.value() == sd::SdCard::SLOT_0) and
(sdInfo.active == sd::SdCard::SLOT_1)) or
((currentlyActiveSdc.value() == sd::SdCard::SLOT_1) and
(sdInfo.active == sd::SdCard::SLOT_0))) {
sdInfo.lockSdCardUsage = true;
}
if (sdInfo.lockSdCardUsage) {
sdcMan->markUnusable();
}
if (sdInfo.active != sd::SdCard::SLOT_0 and sdInfo.active != sd::SdCard::SLOT_1) {
sif::warning << "Preferred SD card invalid. Setting to card 0.." << std::endl;
sdInfo.active = sd::SdCard::SLOT_0;
@ -462,7 +464,11 @@ ReturnValue_t CoreController::sdStateMachine() {
sif::info << "Cold redundant SD card configuration, target SD card: "
<< static_cast<int>(sdInfo.active) << std::endl;
}
SdStates tgtState = SdStates::IDLE;
bool skipCycles = sdInfo.lockSdCardUsage;
// Need to do different things depending on state of SD card which will be active.
if (sdInfo.activeState == sd::SdState::MOUNTED) {
// Already mounted, so we can perform handling of the other side.
#if OBSW_VERBOSE_LEVEL >= 1
std::string mountString;
if (sdInfo.active == sd::SdCard::SLOT_0) {
@ -475,27 +481,57 @@ ReturnValue_t CoreController::sdStateMachine() {
#endif
sdcMan->setActiveSdCard(sdInfo.active);
currMntPrefix = sdcMan->getCurrentMountPrefix();
sdFsmState = SdStates::DETERMINE_OTHER;
tgtState = SdStates::DETERMINE_OTHER;
} else if (sdInfo.activeState == sd::SdState::OFF) {
// It's okay to do the delay after swichting active SD on, no one can use it anyway..
sdCardSetup(sdInfo.active, sd::SdState::ON, sdInfo.activeChar, false);
sdInfo.commandExecuted = true;
sdInfo.commandPending = true;
// Do not skip cycles here, would mess up the state machine. We skip the cycles after
// the SD card was switched on.
skipCycles = false;
// Remain on the current state.
tgtState = sdFsmState;
} else if (sdInfo.activeState == sd::SdState::ON) {
sdFsmState = SdStates::MOUNT_SELF;
// We can do the delay before mounting where applicable.
tgtState = SdStates::MOUNT_SELF;
}
if (skipCycles) {
sdFsmState = SdStates::SKIP_TWO_CYCLES_IF_SD_LOCKED;
fsmStateAfterDelay = tgtState;
sdInfo.skippedCyclesCount = 0;
} else {
sdFsmState = tgtState;
}
} else {
if (nonBlockingOpChecking(SdStates::MOUNT_SELF, 10, "Setting SDC state")) {
if (nonBlockingSdcOpChecking(SdStates::MOUNT_SELF, 10, "Setting SDC state")) {
sdInfo.activeState = sd::SdState::ON;
currentStateSetter(sdInfo.active, sd::SdState::ON);
// Skip the two cycles now.
if (sdInfo.lockSdCardUsage) {
sdFsmState = SdStates::SKIP_TWO_CYCLES_IF_SD_LOCKED;
fsmStateAfterDelay = SdStates::MOUNT_SELF;
sdInfo.skippedCyclesCount = 0;
}
}
}
}
if (sdFsmState == SdStates::SKIP_TWO_CYCLES_IF_SD_LOCKED) {
sdInfo.skippedCyclesCount++;
// Count to three because this branch will run in the same FSM cycle.
if (sdInfo.skippedCyclesCount == 3) {
sdFsmState = fsmStateAfterDelay;
fsmStateAfterDelay = SdStates::IDLE;
sdInfo.skippedCyclesCount = 0;
}
}
if (sdFsmState == SdStates::MOUNT_SELF) {
if (not sdInfo.commandExecuted) {
if (not sdInfo.commandPending) {
result = sdCardSetup(sdInfo.active, sd::SdState::MOUNTED, sdInfo.activeChar);
sdInfo.commandExecuted = true;
sdInfo.commandPending = true;
} else {
if (nonBlockingOpChecking(SdStates::DETERMINE_OTHER, 5, "Mounting SD card")) {
if (nonBlockingSdcOpChecking(SdStates::DETERMINE_OTHER, 5, "Mounting SD card")) {
sdcMan->setActiveSdCard(sdInfo.active);
currMntPrefix = sdcMan->getCurrentMountPrefix();
sdInfo.activeState = sd::SdState::MOUNTED;
@ -532,25 +568,35 @@ ReturnValue_t CoreController::sdStateMachine() {
if (sdFsmState == SdStates::SET_STATE_OTHER) {
// Set state of other SD card to ON or OFF, depending on redundancy mode
if (sdInfo.cfgMode == SdCfgMode::COLD_REDUNDANT) {
if (not sdInfo.commandExecuted) {
if (not sdInfo.commandPending) {
result = sdCardSetup(sdInfo.other, sd::SdState::OFF, sdInfo.otherChar, false);
sdInfo.commandExecuted = true;
sdInfo.commandPending = true;
} else {
if (nonBlockingOpChecking(SdStates::SKIP_CYCLE_BEFORE_INFO_UPDATE, 10,
if (nonBlockingSdcOpChecking(SdStates::SKIP_CYCLE_BEFORE_INFO_UPDATE, 10,
"Switching off other SD card")) {
sdInfo.otherState = sd::SdState::OFF;
currentStateSetter(sdInfo.other, sd::SdState::OFF);
} else {
// Continue.. avoid being stuck here..
sdFsmState = SdStates::SKIP_CYCLE_BEFORE_INFO_UPDATE;
sdInfo.otherState = sd::SdState::OFF;
currentStateSetter(sdInfo.other, sd::SdState::OFF);
}
}
} else if (sdInfo.cfgMode == SdCfgMode::HOT_REDUNDANT) {
if (not sdInfo.commandExecuted) {
if (not sdInfo.commandPending) {
result = sdCardSetup(sdInfo.other, sd::SdState::ON, sdInfo.otherChar, false);
sdInfo.commandExecuted = true;
sdInfo.commandPending = true;
} else {
if (nonBlockingOpChecking(SdStates::MOUNT_UNMOUNT_OTHER, 10,
if (nonBlockingSdcOpChecking(SdStates::MOUNT_UNMOUNT_OTHER, 10,
"Switching on other SD card")) {
sdInfo.otherState = sd::SdState::ON;
currentStateSetter(sdInfo.other, sd::SdState::ON);
} else {
// Contnue.. avoid being stuck here.
sdFsmState = SdStates::MOUNT_UNMOUNT_OTHER;
sdInfo.otherState = sd::SdState::ON;
currentStateSetter(sdInfo.other, sd::SdState::ON);
}
}
}
@ -559,21 +605,25 @@ ReturnValue_t CoreController::sdStateMachine() {
if (sdFsmState == SdStates::MOUNT_UNMOUNT_OTHER) {
// Mount or unmount other SD card, depending on redundancy mode
if (sdInfo.cfgMode == SdCfgMode::COLD_REDUNDANT) {
if (not sdInfo.commandExecuted) {
if (not sdInfo.commandPending) {
result = sdCardSetup(sdInfo.other, sd::SdState::ON, sdInfo.otherChar);
sdInfo.commandExecuted = true;
sdInfo.commandPending = true;
} else {
if (nonBlockingOpChecking(SdStates::SET_STATE_OTHER, 10, "Unmounting other SD card")) {
if (nonBlockingSdcOpChecking(SdStates::SET_STATE_OTHER, 10, "Unmounting other SD card")) {
sdInfo.otherState = sd::SdState::ON;
currentStateSetter(sdInfo.other, sd::SdState::ON);
} else {
sdInfo.otherState = sd::SdState::ON;
currentStateSetter(sdInfo.other, sd::SdState::ON);
sdFsmState = SdStates::SET_STATE_OTHER;
}
}
} else if (sdInfo.cfgMode == SdCfgMode::HOT_REDUNDANT) {
if (not sdInfo.commandExecuted) {
if (not sdInfo.commandPending) {
result = sdCardSetup(sdInfo.other, sd::SdState::MOUNTED, sdInfo.otherChar);
sdInfo.commandExecuted = true;
sdInfo.commandPending = true;
} else {
if (nonBlockingOpChecking(SdStates::UPDATE_INFO, 4, "Mounting other SD card")) {
if (nonBlockingSdcOpChecking(SdStates::UPDATE_SD_INFO_END, 4, "Mounting other SD card")) {
sdInfo.otherState = sd::SdState::MOUNTED;
currentStateSetter(sdInfo.other, sd::SdState::MOUNTED);
}
@ -582,14 +632,17 @@ ReturnValue_t CoreController::sdStateMachine() {
}
if (sdFsmState == SdStates::SKIP_CYCLE_BEFORE_INFO_UPDATE) {
sdFsmState = SdStates::UPDATE_INFO;
} else if (sdFsmState == SdStates::UPDATE_INFO) {
sdFsmState = SdStates::UPDATE_SD_INFO_END;
} else if (sdFsmState == SdStates::UPDATE_SD_INFO_END) {
// Update status file
result = sdcMan->updateSdCardStateFile();
if (result != returnvalue::OK) {
sif::warning << "CoreController::initialize: Updating SD card state file failed" << std::endl;
sif::warning << "CoreController: Updating SD card state file failed" << std::endl;
}
sdInfo.commandExecuted = false;
updateInternalSdInfo();
// Mark usable again in any case.
sdcMan->markUsable();
sdInfo.commandPending = false;
sdFsmState = SdStates::IDLE;
sdInfo.cycleCount = 0;
sdcMan->setBlocking(false);
@ -599,8 +652,16 @@ ReturnValue_t CoreController::sdStateMachine() {
actionHelper.finish(true, sdCommandingInfo.commander, sdCommandingInfo.actionId,
returnvalue::OK);
}
const char *modeStr = "UNKNOWN";
if (sdInfo.cfgMode == SdCfgMode::COLD_REDUNDANT) {
modeStr = "COLD REDUNDANT";
} else if (sdInfo.cfgMode == SdCfgMode::HOT_REDUNDANT) {
modeStr = "HOT REDUNDANT";
}
sif::info << "SD card update into " << modeStr
<< " mode finished. Active SD: " << sdInfo.activeChar << std::endl;
if (not sdInfo.initFinished) {
updateSdInfoOther();
updateInternalSdInfo();
sdInfo.initFinished = true;
sif::info << "SD card initialization finished" << std::endl;
}
@ -977,7 +1038,7 @@ ReturnValue_t CoreController::gracefulShutdownTasks(xsc::Chip chip, xsc::Copy co
return result;
}
void CoreController::updateSdInfoOther() {
void CoreController::updateInternalSdInfo() {
if (sdInfo.active == sd::SdCard::SLOT_0) {
sdInfo.activeChar = "0";
sdInfo.otherChar = "1";
@ -1923,7 +1984,14 @@ ReturnValue_t CoreController::executeSwUpdate(SwUpdateSources sourceDir, const u
} else if (sourceDir == SwUpdateSources::TMP_DIR) {
prefixPath = path("/tmp");
}
path archivePath = prefixPath / path(config::OBSW_UPDATE_ARCHIVE_FILE_NAME);
path archivePath;
// It is optionally possible to supply the source file path
if (size > 2) {
archivePath = prefixPath / std::string(reinterpret_cast<const char *>(data + 2), size - 2);
} else {
archivePath = prefixPath / path(config::OBSW_UPDATE_ARCHIVE_FILE_NAME);
}
sif::info << "Updating with archive path " << archivePath << std::endl;
std::error_code e;
if (not exists(archivePath, e)) {
return HasFileSystemIF::FILE_DOES_NOT_EXIST;
@ -2010,6 +2078,10 @@ ReturnValue_t CoreController::executeSwUpdate(SwUpdateSources sourceDir, const u
cmd.str("");
cmd.clear();
// Remove the extracted files to keep directories clean.
std::filesystem::remove(strippedImagePath, e);
std::filesystem::remove(obswVersionFilePath, e);
// TODO: This takes a long time and will block the core controller.. Maybe use command executor?
// For now dont care..
cmd << "writeprotect " << std::to_string(data[0]) << " " << std::to_string(data[1]) << " 1";
@ -2030,6 +2102,11 @@ bool CoreController::startSdStateMachine(sd::SdCard targetActiveSd, SdCfgMode mo
}
sdFsmState = SdStates::START;
sdInfo.active = targetActiveSd;
// If we are going from 2 SD cards to one, lock SD card usage in any case because 1 SD card is
// going off.
if (sdInfo.cfgMode == SdCfgMode::HOT_REDUNDANT and mode == SdCfgMode::COLD_REDUNDANT) {
sdInfo.lockSdCardUsage = true;
}
sdInfo.cfgMode = mode;
sdCommandingInfo.actionId = actionId;
sdCommandingInfo.commander = commander;

View File

@ -129,8 +129,8 @@ class CoreController : public ExtendedControllerBase {
enum class SdStates {
NONE,
START,
GET_INFO,
SET_STATE_SELF,
UPDATE_SD_INFO_START,
SKIP_TWO_CYCLES_IF_SD_LOCKED,
MOUNT_SELF,
// Determine operations for other SD card, depending on redundancy configuration
DETERMINE_OTHER,
@ -140,7 +140,7 @@ class CoreController : public ExtendedControllerBase {
// Skip period because the shell command used to generate the info file sometimes is
// missing the last performed operation if executed too early
SKIP_CYCLE_BEFORE_INFO_UPDATE,
UPDATE_INFO,
UPDATE_SD_INFO_END,
// SD initialization done
IDLE
};
@ -153,22 +153,28 @@ class CoreController : public ExtendedControllerBase {
MessageQueueIF* eventQueue = nullptr;
SdStates sdFsmState = SdStates::START;
SdStates fsmStateAfterDelay = SdStates::IDLE;
enum SdCfgMode { PASSIVE, COLD_REDUNDANT, HOT_REDUNDANT };
struct SdFsmParams {
SdCfgMode cfgMode = SdCfgMode::COLD_REDUNDANT;
sd::SdCard active = sd::SdCard::NONE;
sd::SdCard other = sd::SdCard::NONE;
sd::SdState activeState = sd::SdState::OFF;
sd::SdState otherState = sd::SdState::OFF;
std::string activeChar = "0";
std::string otherChar = "1";
sd::SdState activeState = sd::SdState::OFF;
sd::SdState otherState = sd::SdState::OFF;
std::pair<bool, bool> mountSwitch = {true, true};
// Used to track whether a command was executed
bool commandExecuted = true;
// This flag denotes that the SD card usage is locked. This is relevant if SD cards go off
// to leave appliation using the SD cards some time to detect the SD card is not usable anymore.
// This is relevant if the active SD card is being switched. The SD card will also be locked
// when going from hot-redundant mode to cold-redundant mode.
bool lockSdCardUsage = false;
bool commandPending = true;
bool initFinished = false;
SdCardManager::SdStatePair currentState;
uint16_t cycleCount = 0;
uint16_t skippedCyclesCount = 0;
} sdInfo;
struct SdCommanding {
@ -232,7 +238,7 @@ class CoreController : public ExtendedControllerBase {
void initPrint();
ReturnValue_t sdStateMachine();
void updateSdInfoOther();
void updateInternalSdInfo();
ReturnValue_t sdCardSetup(sd::SdCard sdCard, sd::SdState targetState, std::string sdChar,
bool printOutput = true);
ReturnValue_t executeSwUpdate(SwUpdateSources sourceDir, const uint8_t* data, size_t size);

View File

@ -68,6 +68,7 @@ void ObjectFactory::produce(void* args) {
#else
createPcduComponents(gpioComIF, &pwrSwitcher, enableHkSets);
#endif
satsystem::EIVE_SYSTEM.setI2cRecoveryParams(pwrSwitcher);
dummy::createDummies(dummyCfg, *pwrSwitcher, gpioComIF);

View File

@ -309,32 +309,6 @@ void SdCardManager::resetState() {
currentOp = Operations::IDLE;
}
ReturnValue_t SdCardManager::updateSdStatePair() {
using namespace std;
std::error_code e;
if (not filesystem::exists(SD_STATE_FILE, e)) {
return STATUS_FILE_NEXISTS;
}
// Now the file should exist in any case. Still check whether it exists.
fstream sdStatus(SD_STATE_FILE);
if (not sdStatus.good()) {
return STATUS_FILE_NEXISTS;
}
string line;
uint8_t idx = 0;
sd::SdCard currentSd = sd::SdCard::SLOT_0;
// Process status file line by line
while (std::getline(sdStatus, line)) {
processSdStatusLine(line, idx, currentSd);
}
if (sdStates.first != sd::SdState::MOUNTED && sdStates.second != sd::SdState::MOUNTED) {
sdCardActive = false;
}
return returnvalue::OK;
}
void SdCardManager::processSdStatusLine(std::string& line, uint8_t& idx, sd::SdCard& currentSd) {
using namespace std;
istringstream iss(line);
@ -407,6 +381,7 @@ ReturnValue_t SdCardManager::setPreferredSdCard(sd::SdCard sdCard) {
}
ReturnValue_t SdCardManager::updateSdCardStateFile() {
using namespace std;
if (cmdExecutor.getCurrentState() == CommandExecutor::States::PENDING) {
return CommandExecutor::COMMAND_PENDING;
}
@ -414,10 +389,31 @@ ReturnValue_t SdCardManager::updateSdCardStateFile() {
std::string updateCmd = "q7hw sd info all > " + std::string(SD_STATE_FILE);
cmdExecutor.load(updateCmd, true, printCmdOutput);
ReturnValue_t result = cmdExecutor.execute();
if (blocking and result != returnvalue::OK) {
if (result != returnvalue::OK) {
utility::handleSystemError(cmdExecutor.getLastError(), "SdCardManager::mountSdCard");
}
return result;
std::error_code e;
if (not filesystem::exists(SD_STATE_FILE, e)) {
return STATUS_FILE_NEXISTS;
}
// Now the file should exist in any case. Still check whether it exists.
fstream sdStatus(SD_STATE_FILE);
if (not sdStatus.good()) {
return STATUS_FILE_NEXISTS;
}
string line;
uint8_t idx = 0;
sd::SdCard currentSd = sd::SdCard::SLOT_0;
// Process status file line by line
while (std::getline(sdStatus, line)) {
processSdStatusLine(line, idx, currentSd);
}
if (sdStates.first != sd::SdState::MOUNTED && sdStates.second != sd::SdState::MOUNTED) {
sdCardActive = false;
}
return returnvalue::OK;
}
const char* SdCardManager::getCurrentMountPrefix() const {
@ -585,3 +581,8 @@ void SdCardManager::markUnusable() {
MutexGuard mg(defaultLock, LOCK_TYPE, OTHER_TIMEOUT, LOCK_CTX);
markedUnusable = true;
}
void SdCardManager::markUsable() {
MutexGuard mg(defaultLock, LOCK_TYPE, OTHER_TIMEOUT, LOCK_CTX);
markedUnusable = false;
}

View File

@ -117,16 +117,6 @@ class SdCardManager : public SystemObject, public SdCardMountedIF {
ReturnValue_t switchOffSdCard(sd::SdCard sdCard, bool doUnmountSdCard = true,
SdStatePair* statusPair = nullptr);
/**
* Update the state file or creates one if it does not exist. You need to call this
* function before calling #sdCardActive
* @return
* - returnvalue::OK if the state file was updated successfully
* - CommandExecutor::COMMAND_PENDING: Non-blocking command is pending
* - returnvalue::FAILED: blocking command failed
*/
ReturnValue_t updateSdCardStateFile();
/**
* Get the state of the SD cards. If the state file does not exist, this function will
* take care of updating it. If it does not, the function will use the state file to get
@ -215,6 +205,7 @@ class SdCardManager : public SystemObject, public SdCardMountedIF {
ReturnValue_t performFsck(sd::SdCard sdcard, bool printOutput, int& linuxError);
void markUnusable();
void markUsable();
private:
CommandExecutor cmdExecutor;
@ -234,7 +225,15 @@ class SdCardManager : public SystemObject, public SdCardMountedIF {
SdCardManager();
ReturnValue_t updateSdStatePair();
/**
* Update the state file or creates one if it does not exist. You need to call this
* function before calling #sdCardActive
* @return
* - returnvalue::OK if the state file was updated successfully
* - CommandExecutor::COMMAND_PENDING: Non-blocking command is pending
* - returnvalue::FAILED: blocking command failed
*/
ReturnValue_t updateSdCardStateFile();
ReturnValue_t setSdCardState(sd::SdCard sdCard, bool on);

View File

@ -40,6 +40,7 @@
#include "mission/system/com/comModeTree.h"
#include "mission/system/tree/payloadModeTree.h"
#include "mission/system/tree/tcsModeTree.h"
#include "mission/tcs/defs.h"
void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpioIF) {
new ComIFDummy(objects::DUMMY_COM_IF);
@ -202,7 +203,8 @@ void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpio
new TemperatureSensorInserter(objects::THERMAL_TEMP_INSERTER, rtdSensorDummies,
tmpSensorDummies);
TcsBoardAssembly* tcsBoardAssy = ObjectFactory::createTcsBoardAssy(pwrSwitcher);
TcsBoardAssembly* tcsBoardAssy =
ObjectFactory::createTcsBoardAssy(pwrSwitcher, tcs::TCS_BOARD_SHORTLY_UNAVAILABLE);
for (auto& rtd : rtdSensorDummies) {
rtd.second->connectModeTreeParent(*tcsBoardAssy);
}

2
fsfw

Submodule fsfw updated: 285d327b97...ffa2fa477f

View File

@ -29,6 +29,7 @@
#include "mission/system/acs/acsModeTree.h"
#include "mission/system/tree/payloadModeTree.h"
#include "mission/system/tree/tcsModeTree.h"
#include "mission/tcs/defs.h"
void ObjectFactory::createSunSensorComponents(GpioIF* gpioComIF, SpiComIF* spiComIF,
PowerSwitchIF& pwrSwitcher, std::string spiDev,
@ -278,7 +279,8 @@ void ObjectFactory::createRtdComponents(std::string spiDev, GpioIF* gpioComIF,
std::array<Max31865EiveHandler*, NUM_RTDS> rtds = {};
RtdFdir* rtdFdir = nullptr;
TcsBoardAssembly* tcsBoardAss = ObjectFactory::createTcsBoardAssy(*pwrSwitcher);
TcsBoardAssembly* tcsBoardAss =
ObjectFactory::createTcsBoardAssy(*pwrSwitcher, tcs::TCS_BOARD_SHORTLY_UNAVAILABLE);
// Create special low level reader communication interface
new Max31865RtdPolling(objects::SPI_RTD_COM_IF, comIF, gpioComIF);

View File

@ -113,7 +113,8 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
if (req->mode != adis.mode) {
if (req->mode == acs::SimpleSensorMode::NORMAL) {
adis.type = req->type;
adis.countdown.setTimeout(adis1650x::START_UP_TIME);
// The initial countdown is handled by the device handler now.
// adis.countdown.setTimeout(adis1650x::START_UP_TIME);
if (adis.type == adis1650x::Type::ADIS16507) {
adis.ownReply.data.accelScaling = adis1650x::ACCELEROMETER_RANGE_16507;
} else if (adis.type == adis1650x::Type::ADIS16505) {
@ -127,6 +128,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
adis.ownReply.cfgWasSet = false;
adis.ownReply.dataWasSet = false;
}
adis.replyResult = returnvalue::FAILED;
adis.mode = req->mode;
}
return returnvalue::OK;
@ -145,6 +147,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
} else {
gyro.ownReply.cfgWasSet = false;
}
gyro.replyResult = returnvalue::FAILED;
gyro.mode = req->mode;
}
return returnvalue::OK;
@ -163,6 +166,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
mgm.ownReply.dataWasSet = false;
mgm.ownReply.temperatureWasSet = false;
}
mgm.replyResult = returnvalue::FAILED;
mgm.mode = req->mode;
}
return returnvalue::OK;
@ -180,6 +184,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
} else {
mgm.ownReply.dataWasRead = false;
}
mgm.replyResult = returnvalue::FAILED;
mgm.mode = req->mode;
}
return returnvalue::OK;
@ -309,18 +314,18 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) {
std::memcpy(cmdBuf.data() + 1, l3g.sensorCfg, 5);
result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6);
if (result != returnvalue::OK) {
l3g.replyResult = returnvalue::OK;
l3g.replyResult = result;
}
// Ignore useless reply and red config
cmdBuf[0] = l3gd20h::CTRL_REG_1 | l3gd20h::AUTO_INCREMENT_MASK | l3gd20h::READ_MASK;
std::memset(cmdBuf.data() + 1, 0, 5);
result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6);
if (result != returnvalue::OK) {
l3g.replyResult = returnvalue::OK;
l3g.replyResult = result;
}
result = spiComIF.readReceivedMessage(l3g.cookie, &rawReply, &dummy);
if (result != returnvalue::OK) {
l3g.replyResult = returnvalue::OK;
l3g.replyResult = result;
}
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
// Cross check configuration as verification that communication is working
@ -331,6 +336,7 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) {
return;
}
}
l3g.replyResult = returnvalue::OK;
l3g.performStartup = false;
l3g.ownReply.cfgWasSet = true;
l3g.ownReply.sensitivity = l3gd20h::ctrlReg4ToSensitivity(l3g.sensorCfg[3]);
@ -357,6 +363,7 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) {
return;
}
}
l3g.replyResult = returnvalue::OK;
l3g.ownReply.statusReg = rawReply[l3gd20h::STATUS_IDX];
l3g.ownReply.angVelocities[0] = (rawReply[l3gd20h::OUT_X_H] << 8) | rawReply[l3gd20h::OUT_X_L];
l3g.ownReply.angVelocities[1] = (rawReply[l3gd20h::OUT_Y_H] << 8) | rawReply[l3gd20h::OUT_Y_L];
@ -443,20 +450,15 @@ ReturnValue_t AcsBoardPolling::readAdisCfg(SpiCookie& cookie, size_t transferLen
void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) {
ReturnValue_t result;
acs::SimpleSensorMode mode = acs::SimpleSensorMode::OFF;
bool cdHasTimedOut = false;
bool mustPerformStartup = false;
{
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
mode = gyro.mode;
cdHasTimedOut = gyro.countdown.hasTimedOut();
mustPerformStartup = gyro.performStartup;
}
if (mode == acs::SimpleSensorMode::OFF) {
return;
}
if (not cdHasTimedOut) {
return;
}
if (mustPerformStartup) {
uint8_t regList[6];
// Read configuration
@ -495,6 +497,7 @@ void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) {
gyro.ownReply.cfg.prodId = prodId;
gyro.ownReply.data.sensitivity = adis1650x::rangMdlToSensitivity(gyro.ownReply.cfg.rangMdl);
gyro.performStartup = false;
gyro.replyResult = returnvalue::OK;
}
// Read regular registers
std::memcpy(cmdBuf.data(), adis1650x::BURST_READ_ENABLE.data(),
@ -533,6 +536,7 @@ void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) {
}
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
gyro.replyResult = returnvalue::OK;
gyro.ownReply.dataWasSet = true;
gyro.ownReply.cfg.diagStat = rawReply[2] << 8 | rawReply[3];
gyro.ownReply.data.angVelocities[0] = (rawReply[4] << 8) | rawReply[5];
@ -590,6 +594,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) {
}
// Done here. We can always read back config and data during periodic handling
mgm.performStartup = false;
mgm.replyResult = returnvalue::OK;
}
cmdBuf[0] = mgmLis3::readCommand(mgmLis3::CTRL_REG1, true);
std::memset(cmdBuf.data() + 1, 0, mgmLis3::NR_OF_DATA_AND_CFG_REGISTERS);
@ -607,7 +612,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) {
// Verify communication by re-checking config
if (rawReply[1] != mgm.cfg[0] or rawReply[2] != mgm.cfg[1] or rawReply[3] != mgm.cfg[2] or
rawReply[4] != mgm.cfg[3] or rawReply[5] != mgm.cfg[4]) {
mgm.replyResult = result;
mgm.replyResult = returnvalue::FAILED;
return;
}
{
@ -634,6 +639,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) {
return;
}
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
mgm.replyResult = returnvalue::OK;
mgm.ownReply.temperatureWasSet = true;
mgm.ownReply.temperatureRaw = (rawReply[2] << 8) | rawReply[1];
}
@ -704,6 +710,7 @@ void AcsBoardPolling::mgmRm3100Handler(MgmRm3100& mgm) {
return;
}
mgm.performStartup = false;
mgm.replyResult = returnvalue::OK;
}
// Regular read operation
cmdBuf[0] = mgmRm3100::MEASUREMENT_REG_START | mgmRm3100::READ_MASK;
@ -725,6 +732,7 @@ void AcsBoardPolling::mgmRm3100Handler(MgmRm3100& mgm) {
mgm.ownReply.scaleFactors[idx] = 1.0 / mgmRm3100::DEFAULT_GAIN;
}
mgm.ownReply.dataWasRead = true;
mgm.replyResult = returnvalue::OK;
// Bitshift trickery to account for 24 bit signed value.
mgm.ownReply.mgmValuesRaw[0] =
((rawReply[1] << 24) | (rawReply[2] << 16) | (rawReply[3] << 8)) >> 8;

View File

@ -73,6 +73,7 @@ ReturnValue_t SusPolling::sendMessage(CookieIF* cookie, const uint8_t* sendData,
susDevs[susIdx].ownReply.cfgWasSet = false;
susDevs[susIdx].ownReply.dataWasSet = false;
}
susDevs[susIdx].replyResult = returnvalue::FAILED;
susDevs[susIdx].mode = susReq->mode;
}
if (state == InternalState::IDLE) {
@ -95,11 +96,14 @@ ReturnValue_t SusPolling::readReceivedMessage(CookieIF* cookie, uint8_t** buffer
if (susIdx < 0) {
return FAILED;
}
if (susDevs[susIdx].replyResult != returnvalue::OK) {
return susDevs[susIdx].replyResult;
}
MutexGuard mg(ipcLock);
std::memcpy(&susDevs[susIdx].readerReply, &susDevs[susIdx].ownReply, sizeof(acs::SusReply));
*buffer = reinterpret_cast<uint8_t*>(&susDevs[susIdx].readerReply);
*size = sizeof(acs::SusReply);
return OK;
return susDevs[susIdx].replyResult;
}
ReturnValue_t SusPolling::handleSusPolling() {
@ -164,6 +168,12 @@ ReturnValue_t SusPolling::handleSusPolling() {
}
MutexGuard mg(ipcLock);
susDevs[idx].ownReply.tempRaw = ((rawReply[0] & 0x0f) << 8) | rawReply[1];
// Reply is all ones. Sensor is probably off or faulty when
// it should not be.
if (susDevs[idx].ownReply.tempRaw == 0x0fff) {
susDevs[idx].replyResult = returnvalue::FAILED;
} else {
susDevs[idx].replyResult = returnvalue::OK;
for (unsigned chIdx = 0; chIdx < 6; chIdx++) {
susDevs[idx].ownReply.channelsRaw[chIdx] =
(rawReply[chIdx * 2 + 2] << 8) | rawReply[chIdx * 2 + 3];
@ -171,6 +181,7 @@ ReturnValue_t SusPolling::handleSusPolling() {
susDevs[idx].ownReply.dataWasSet = true;
}
}
}
return OK;
}

View File

@ -56,6 +56,9 @@ ReturnValue_t GyrAdis1650XHandler::buildNormalDeviceCommand(DeviceCommandId_t *i
ReturnValue_t GyrAdis1650XHandler::buildTransitionDeviceCommand(DeviceCommandId_t *id) {
switch (internalState) {
case (InternalState::STARTUP): {
if (breakCountdown.isBusy()) {
return NOTHING_TO_SEND;
}
*id = adis1650x::REQUEST;
return preparePeriodicRequest(acs::SimpleSensorMode::NORMAL);
}

View File

@ -1 +1,3 @@
target_sources(
${LIB_EIVE_MISSION} PRIVATE CfdpHandler.cpp)

View File

@ -0,0 +1,134 @@
#include "CfdpHandler.h"
#include "fsfw/cfdp/pdu/AckPduReader.h"
#include "fsfw/cfdp/pdu/PduHeaderReader.h"
#include "fsfw/globalfunctions/arrayprinter.h"
#include "fsfw/ipc/QueueFactory.h"
#include "fsfw/tmtcservices/TmTcMessage.h"
using namespace returnvalue;
using namespace cfdp;
CfdpHandler::CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg)
: SystemObject(fsfwParams.objectId),
msgQueue(fsfwParams.msgQueue),
destHandler(
DestHandlerParams(LocalEntityCfg(cfdpCfg.id, cfdpCfg.indicCfg, cfdpCfg.faultHandler),
cfdpCfg.userHandler, cfdpCfg.remoteCfgProvider, cfdpCfg.packetInfoList,
cfdpCfg.lostSegmentsList),
FsfwParams(fsfwParams.packetDest, nullptr, this, fsfwParams.tcStore,
fsfwParams.tmStore)) {
destHandler.setMsgQueue(msgQueue);
}
[[nodiscard]] const char* CfdpHandler::getName() const { return "CFDP Handler"; }
[[nodiscard]] uint32_t CfdpHandler::getIdentifier() const {
return destHandler.getDestHandlerParams().cfg.localId.getValue();
}
[[nodiscard]] MessageQueueId_t CfdpHandler::getRequestQueue() const { return msgQueue.getId(); }
ReturnValue_t CfdpHandler::initialize() {
ReturnValue_t result = destHandler.initialize();
if (result != OK) {
return result;
}
tcStore = destHandler.getTcStore();
tmStore = destHandler.getTmStore();
return SystemObject::initialize();
}
ReturnValue_t CfdpHandler::performOperation(uint8_t operationCode) {
// TODO: Receive TC packets and route them to source and dest handler, depending on which is
// correct or more appropriate
ReturnValue_t status;
ReturnValue_t result = OK;
TmTcMessage tmtcMsg;
for (status = msgQueue.receiveMessage(&tmtcMsg); status == returnvalue::OK;
status = msgQueue.receiveMessage(&tmtcMsg)) {
result = handleCfdpPacket(tmtcMsg);
if (result != OK) {
status = result;
}
}
auto& fsmRes = destHandler.performStateMachine();
// TODO: Error handling?
while (fsmRes.callStatus == CallStatus::CALL_AGAIN) {
destHandler.performStateMachine();
// TODO: Error handling?
}
return status;
}
ReturnValue_t CfdpHandler::handleCfdpPacket(TmTcMessage& msg) {
auto accessorPair = tcStore->getData(msg.getStorageId());
if (accessorPair.first != OK) {
return accessorPair.first;
}
PduHeaderReader reader(accessorPair.second.data(), accessorPair.second.size());
ReturnValue_t result = reader.parseData();
if (result != returnvalue::OK) {
return INVALID_PDU_FORMAT;
}
// The CFDP distributor should have taken care of ensuring the destination ID is correct
PduType type = reader.getPduType();
// Only the destination handler can process these PDUs
if (type == PduType::FILE_DATA) {
// Disable auto-deletion of packet
accessorPair.second.release();
PacketInfo info(type, msg.getStorageId());
result = destHandler.passPacket(info);
} else {
// Route depending on PDU type and directive type if applicable. It retrieves directive type
// from the raw stream for better performance (with sanity and directive code check).
// The routing is based on section 4.5 of the CFDP standard which specifies the PDU forwarding
// procedure.
// PDU header only. Invalid supplied data. A directive packet should have a valid data field
// with at least one byte being the directive code
const uint8_t* pduDataField = reader.getPduDataField();
if (pduDataField == nullptr) {
return INVALID_PDU_FORMAT;
}
if (not FileDirectiveReader::checkFileDirective(pduDataField[0])) {
return INVALID_DIRECTIVE_FIELD;
}
auto directive = static_cast<FileDirective>(pduDataField[0]);
auto passToDestHandler = [&]() {
accessorPair.second.release();
PacketInfo info(type, msg.getStorageId(), directive);
result = destHandler.passPacket(info);
};
auto passToSourceHandler = [&]() {
};
if (directive == FileDirective::METADATA or directive == FileDirective::EOF_DIRECTIVE or
directive == FileDirective::PROMPT) {
// Section b) of 4.5.3: These PDUs should always be targeted towards the file receiver a.k.a.
// the destination handler
passToDestHandler();
} else if (directive == FileDirective::FINISH or directive == FileDirective::NAK or
directive == FileDirective::KEEP_ALIVE) {
// Section c) of 4.5.3: These PDUs should always be targeted towards the file sender a.k.a.
// the source handler
passToSourceHandler();
} else if (directive == FileDirective::ACK) {
// Section a): Recipient depends of the type of PDU that is being acknowledged. We can simply
// extract the PDU type from the raw stream. If it is an EOF PDU, this packet is passed to
// the source handler, for a Finished PDU, it is passed to the destination handler.
FileDirective ackedDirective;
if (not AckPduReader::checkAckedDirectiveField(pduDataField[1], ackedDirective)) {
return INVALID_ACK_DIRECTIVE_FIELDS;
}
if (ackedDirective == FileDirective::EOF_DIRECTIVE) {
passToSourceHandler();
} else if (ackedDirective == FileDirective::FINISH) {
passToDestHandler();
}
}
}
return result;
}

View File

@ -0,0 +1,71 @@
#ifndef FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
#define FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H
#include <utility>
#include "fsfw/cfdp/handler/DestHandler.h"
#include "fsfw/objectmanager/SystemObject.h"
#include "fsfw/tasks/ExecutableObjectIF.h"
#include "fsfw/tmtcservices/AcceptsTelecommandsIF.h"
#include "fsfw/tmtcservices/TmTcMessage.h"
struct FsfwHandlerParams {
FsfwHandlerParams(object_id_t objectId, HasFileSystemIF& vfs, AcceptsTelemetryIF& packetDest,
StorageManagerIF& tcStore, StorageManagerIF& tmStore, MessageQueueIF& msgQueue)
: objectId(objectId),
vfs(vfs),
packetDest(packetDest),
tcStore(tcStore),
tmStore(tmStore),
msgQueue(msgQueue) {}
object_id_t objectId{};
HasFileSystemIF& vfs;
AcceptsTelemetryIF& packetDest;
StorageManagerIF& tcStore;
StorageManagerIF& tmStore;
MessageQueueIF& msgQueue;
};
struct CfdpHandlerCfg {
CfdpHandlerCfg(cfdp::EntityId localId, cfdp::IndicationCfg indicationCfg,
cfdp::UserBase& userHandler, cfdp::FaultHandlerBase& userFaultHandler,
cfdp::PacketInfoListBase& packetInfo, cfdp::LostSegmentsListBase& lostSegmentsList,
cfdp::RemoteConfigTableIF& remoteCfgProvider)
: id(std::move(localId)),
indicCfg(indicationCfg),
packetInfoList(packetInfo),
lostSegmentsList(lostSegmentsList),
remoteCfgProvider(remoteCfgProvider),
userHandler(userHandler),
faultHandler(userFaultHandler) {}
cfdp::EntityId id;
cfdp::IndicationCfg indicCfg;
cfdp::PacketInfoListBase& packetInfoList;
cfdp::LostSegmentsListBase& lostSegmentsList;
cfdp::RemoteConfigTableIF& remoteCfgProvider;
cfdp::UserBase& userHandler;
cfdp::FaultHandlerBase& faultHandler;
};
class CfdpHandler : public SystemObject, public ExecutableObjectIF, public AcceptsTelecommandsIF {
public:
explicit CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg);
[[nodiscard]] const char* getName() const override;
[[nodiscard]] uint32_t getIdentifier() const override;
[[nodiscard]] MessageQueueId_t getRequestQueue() const override;
ReturnValue_t initialize() override;
ReturnValue_t performOperation(uint8_t operationCode) override;
private:
MessageQueueIF& msgQueue;
cfdp::DestHandler destHandler;
StorageManagerIF* tcStore = nullptr;
StorageManagerIF* tmStore = nullptr;
ReturnValue_t handleCfdpPacket(TmTcMessage& msg);
};
#endif // FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H

View File

@ -21,7 +21,8 @@
#define LOWER_EBAND_UPPER_LIMITS 0
#define LOWER_PLOC_UPPER_LIMITS 0
ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater)
ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater,
const std::atomic_bool& tcsBoardShortUnavailable)
: ExtendedControllerBase(objectId),
heaterHandler(heater),
sensorTemperatures(this),
@ -66,7 +67,8 @@ ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater
susSet8(objects::SUS_8_R_LOC_XBYBZB_PT_YB),
susSet9(objects::SUS_9_R_LOC_XBYBZB_PT_YF),
susSet10(objects::SUS_10_N_LOC_XMYBZF_PT_ZF),
susSet11(objects::SUS_11_R_LOC_XBYMZB_PT_ZB) {
susSet11(objects::SUS_11_R_LOC_XBYMZB_PT_ZB),
tcsBrdShortlyUnavailable(tcsBoardShortUnavailable) {
resetSensorsArray();
}
@ -134,12 +136,14 @@ void ThermalController::performControlOperation() {
}
}
if (not tcsBrdShortlyUnavailable) {
{
PoolReadGuard pg(&sensorTemperatures);
if (pg.getReadResult() == returnvalue::OK) {
copySensors();
}
}
}
{
PoolReadGuard pg(&susTemperatures);
if (pg.getReadResult() == returnvalue::OK) {
@ -182,7 +186,7 @@ void ThermalController::performControlOperation() {
}
setMode(MODE_OFF);
}
} else if (mode != MODE_OFF) {
} else if (mode != MODE_OFF and not tcsBrdShortlyUnavailable) {
performThermalModuleCtrl(heaterSwitchStateArray);
}
cycles++;

View File

@ -22,6 +22,7 @@
#include <mission/tcs/Tmp1075Definitions.h>
#include <mission/utility/trace.h>
#include <atomic>
#include <list>
/**
@ -94,7 +95,8 @@ class ThermalController : public ExtendedControllerBase {
static constexpr int16_t SANITY_LIMIT_LOWER_TEMP = -80;
static constexpr int16_t SANITY_LIMIT_UPPER_TEMP = 160;
ThermalController(object_id_t objectId, HeaterHandler& heater);
ThermalController(object_id_t objectId, HeaterHandler& heater,
const std::atomic_bool& tcsBoardShortUnavailable);
ReturnValue_t initialize() override;
@ -181,6 +183,10 @@ class ThermalController : public ExtendedControllerBase {
susMax1227::SusDataset susSet10;
susMax1227::SusDataset susSet11;
// If the TCS board in unavailable, for example due to a recovery, skip
// some TCS controller tasks to avoid unnecessary events.
const std::atomic_bool& tcsBrdShortlyUnavailable = false;
lp_var_t<float> tempQ7s = lp_var_t<float>(objects::CORE_CONTROLLER, core::PoolIds::TEMPERATURE);
lp_var_t<int16_t> battTemp1 = lp_var_t<int16_t>(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_1);
lp_var_t<int16_t> battTemp2 = lp_var_t<int16_t>(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_2);

View File

@ -1,5 +1,5 @@
#include <fsfw/cfdp/CfdpDistributor.h>
#include <fsfw/cfdp/handler/CfdpHandler.h>
#include <mission/cfdp/CfdpHandler.h>
#include <fsfw/cfdp/handler/RemoteConfigTableIF.h>
#include <fsfw/controller/ControllerBase.h>
#include <fsfw/controller/ExtendedControllerBase.h>
@ -48,6 +48,7 @@
#include "mission/system/acs/RwAssembly.h"
#include "mission/system/acs/acsModeTree.h"
#include "mission/system/tree/tcsModeTree.h"
#include "mission/tcs/defs.h"
#include "mission/tmtc/tmFilters.h"
#include "objects/systemObjectList.h"
#include "tmtc/pusIds.h"
@ -90,6 +91,8 @@ EiveFaultHandler EIVE_FAULT_HANDLER;
} // namespace cfdp
std::atomic_bool tcs::TCS_BOARD_SHORTLY_UNAVAILABLE = false;
void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFunnel** pusFunnel,
CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan,
StorageManagerIF** ipcStore, StorageManagerIF** tmStore,
@ -300,7 +303,8 @@ void ObjectFactory::createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF&
}
void ObjectFactory::createThermalController(HeaterHandler& heaterHandler) {
auto* tcsCtrl = new ThermalController(objects::THERMAL_CONTROLLER, heaterHandler);
auto* tcsCtrl = new ThermalController(objects::THERMAL_CONTROLLER, heaterHandler,
tcs::TCS_BOARD_SHORTLY_UNAVAILABLE);
tcsCtrl->connectModeTreeParent(satsystem::tcs::SUBSYSTEM);
}
void ObjectFactory::createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch,
@ -366,10 +370,12 @@ void ObjectFactory::createAcsBoardAssy(PowerSwitchIF& pwrSwitcher,
acsAss->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM);
}
TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher) {
TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher,
std::atomic_bool& tcsShortlyUnavailable) {
TcsBoardHelper helper(RTD_INFOS);
auto* tcsBoardAss = new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher,
power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper);
auto* tcsBoardAss =
new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher,
power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper, tcsShortlyUnavailable);
tcsBoardAss->connectModeTreeParent(satsystem::tcs::SUBSYSTEM);
return tcsBoardAss;
}

View File

@ -55,7 +55,8 @@ void createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch,
void createSusAssy(PowerSwitchIF& pwrSwitcher, std::array<DeviceHandlerBase*, 12> suses);
void createAcsBoardAssy(PowerSwitchIF& pwrSwitcher, std::array<DeviceHandlerBase*, 8> assemblyDhbs,
ExtendedControllerBase* gpsCtrl, GpioIF* gpioComIF);
TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher);
TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher,
std::atomic_bool& tcsShortlyUnavailable);
} // namespace ObjectFactory

View File

@ -50,6 +50,7 @@ static constexpr Event FDIR_REACTION_IGNORED = event::makeEvent(SUBSYSTEM_ID, 3,
enum class States { IDLE, SWITCHING_POWER, CHECKING_POWER, MODE_COMMANDING };
enum class OpCodes { NONE, TO_OFF_DONE, TO_NOT_OFF_DONE, TIMEOUT_OCCURED };
enum RecoveryCustomStates { IDLE, POWER_SWITCHING_OFF, POWER_SWITCHING_ON, DONE };
} // namespace power
namespace duallane {

View File

@ -6,5 +6,6 @@ add_subdirectory(fdir)
add_subdirectory(power)
target_sources(
${LIB_EIVE_MISSION} PRIVATE systemTree.cpp DualLanePowerStateMachine.cpp
EiveSystem.cpp treeUtil.cpp)
${LIB_EIVE_MISSION}
PRIVATE systemTree.cpp DualLanePowerStateMachine.cpp EiveSystem.cpp
treeUtil.cpp SharedPowerAssemblyBase.cpp)

View File

@ -0,0 +1,91 @@
#include "SharedPowerAssemblyBase.h"
SharedPowerAssemblyBase::SharedPowerAssemblyBase(object_id_t objectId, PowerSwitchIF* pwrSwitcher,
power::Switch_t switchId, uint16_t cmdQueueDepth)
: AssemblyBase(objectId, cmdQueueDepth), switcher(pwrSwitcher, switchId) {}
void SharedPowerAssemblyBase::performChildOperation() {
auto state = switcher.getState();
if (state != PowerSwitcher::WAIT_OFF and state != PowerSwitcher::WAIT_ON) {
AssemblyBase::performChildOperation();
return;
}
switcher.doStateMachine();
if (state == PowerSwitcher::WAIT_OFF and switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
// Indicator that a transition to off is finished
AssemblyBase::handleModeReached();
} else if (state == PowerSwitcher::WAIT_ON and
switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
// Indicator that mode commanding can be performed now
AssemblyBase::startTransition(targetMode, targetSubmode);
}
}
void SharedPowerAssemblyBase::startTransition(Mode_t mode, Submode_t submode) {
if (mode != MODE_OFF) {
switcher.turnOn(true);
switcher.doStateMachine();
if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
AssemblyBase::startTransition(mode, submode);
} else {
// Need to wait with mode commanding until power switcher is done
targetMode = mode;
targetSubmode = submode;
}
} else {
// Perform regular mode commanding first
AssemblyBase::startTransition(mode, submode);
}
}
void SharedPowerAssemblyBase::handleModeReached() {
if (targetMode == MODE_OFF) {
switcher.turnOff(true);
switcher.doStateMachine();
// Need to wait with call to AssemblyBase::handleModeReached until power switcher is done
if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
AssemblyBase::handleModeReached();
}
} else {
AssemblyBase::handleModeReached();
}
}
bool SharedPowerAssemblyBase::checkAndHandleRecovery() {
using namespace power;
if (recoveryState == RECOVERY_IDLE) {
return AssemblyBase::checkAndHandleRecovery();
}
if (customRecoveryStates == IDLE) {
switcher.turnOff();
customRecoveryStates = RecoveryCustomStates::POWER_SWITCHING_OFF;
}
if (customRecoveryStates == POWER_SWITCHING_OFF) {
switcher.doStateMachine();
if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
customRecoveryStates = RecoveryCustomStates::POWER_SWITCHING_ON;
switcher.turnOn();
}
}
if (customRecoveryStates == POWER_SWITCHING_ON) {
switcher.doStateMachine();
if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
customRecoveryStates = RecoveryCustomStates::DONE;
}
}
if (customRecoveryStates == DONE) {
bool pendingRecovery = AssemblyBase::checkAndHandleRecovery();
if (not pendingRecovery) {
customRecoveryStates = RecoveryCustomStates::IDLE;
}
// For a recovery on one side, only do the recovery once
for (auto& child : childrenMap) {
if (healthHelper.healthTable->getHealth(child.first) == HasHealthIF::NEEDS_RECOVERY) {
sendHealthCommand(child.second.commandQueue, HEALTHY);
child.second.healthChanged = false;
}
}
return pendingRecovery;
}
return true;
}

View File

@ -0,0 +1,27 @@
#ifndef MISSION_SYSTEM_SHAREDPOWERASSEMBLYBASE_H_
#define MISSION_SYSTEM_SHAREDPOWERASSEMBLYBASE_H_
#include <fsfw/devicehandlers/AssemblyBase.h>
#include <fsfw/power/PowerSwitcher.h>
#include <mission/power/defs.h>
/**
* Base class which contains common functions for assemblies where the power line is shared
* among the devices in the assembly.
*/
class SharedPowerAssemblyBase : public AssemblyBase {
public:
SharedPowerAssemblyBase(object_id_t objectId, PowerSwitchIF* pwrSwitcher,
power::Switch_t switchId, uint16_t cmdQueueDepth = 8);
protected:
PowerSwitcher switcher;
power::RecoveryCustomStates customRecoveryStates = power::RecoveryCustomStates::IDLE;
void performChildOperation() override;
void startTransition(Mode_t mode, Submode_t submode) override;
void handleModeReached() override;
virtual bool checkAndHandleRecovery() override;
};
#endif /* MISSION_SYSTEM_SHAREDPOWERASSEMBLYBASE_H_ */

View File

@ -39,12 +39,7 @@ class DualLaneAssemblyBase : public AssemblyBase, public ConfirmsFailuresIF {
SideSwitchState sideSwitchState = SideSwitchState::NONE;
duallane::Submodes targetSubmodeForSideSwitch = duallane::Submodes::B_SIDE;
enum RecoveryCustomStates {
IDLE,
POWER_SWITCHING_OFF,
POWER_SWITCHING_ON,
DONE
} customRecoveryStates = RecoveryCustomStates::IDLE;
power::RecoveryCustomStates customRecoveryStates = power::RecoveryCustomStates::IDLE;
MessageQueueIF* eventQueue = nullptr;

View File

@ -1,8 +1,8 @@
#include "RwAssembly.h"
RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher,
RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switchId,
RwHelper helper)
: AssemblyBase(objectId), helper(helper), switcher(pwrSwitcher, switcher) {
: SharedPowerAssemblyBase(objectId, pwrSwitcher, switchId), helper(helper) {
ModeListEntry entry;
for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) {
entry.setObject(helper.rwIds[idx]);
@ -12,26 +12,8 @@ RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::
}
}
void RwAssembly::performChildOperation() {
auto state = switcher.getState();
if (state != PowerSwitcher::WAIT_OFF and state != PowerSwitcher::WAIT_ON) {
AssemblyBase::performChildOperation();
return;
}
switcher.doStateMachine();
if (state == PowerSwitcher::WAIT_OFF and switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
// Indicator that a transition to off is finished
AssemblyBase::handleModeReached();
} else if (state == PowerSwitcher::WAIT_ON and
switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
// Indicator that mode commanding can be performed now
AssemblyBase::startTransition(targetMode, targetSubmode);
}
}
ReturnValue_t RwAssembly::commandChildren(Mode_t mode, Submode_t submode) {
ReturnValue_t result = returnvalue::OK;
modeTransitionFailedSwitch = true;
// Initialize the mode table to ensure all devices are in a defined state
for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) {
modeTable[idx].setMode(MODE_OFF);
@ -76,36 +58,6 @@ ReturnValue_t RwAssembly::isModeCombinationValid(Mode_t mode, Submode_t submode)
return HasModesIF::INVALID_MODE;
}
void RwAssembly::startTransition(Mode_t mode, Submode_t submode) {
if (mode != MODE_OFF) {
switcher.turnOn(true);
switcher.doStateMachine();
if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
AssemblyBase::startTransition(mode, submode);
} else {
// Need to wait with mode commanding until power switcher is done
targetMode = mode;
targetSubmode = submode;
}
} else {
// Perform regular mode commanding first
AssemblyBase::startTransition(mode, submode);
}
}
void RwAssembly::handleModeReached() {
if (targetMode == MODE_OFF) {
switcher.turnOff(true);
switcher.doStateMachine();
// Need to wait with call to AssemblyBase::handleModeReached until power switcher is done
if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
AssemblyBase::handleModeReached();
}
} else {
AssemblyBase::handleModeReached();
}
}
ReturnValue_t RwAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode) {
ReturnValue_t result = returnvalue::OK;
object_id_t objId = 0;

View File

@ -1,8 +1,8 @@
#ifndef MISSION_SYSTEM_RWASS_H_
#define MISSION_SYSTEM_RWASS_H_
#include <fsfw/devicehandlers/AssemblyBase.h>
#include <fsfw/power/PowerSwitcher.h>
#include <mission/system/SharedPowerAssemblyBase.h>
struct RwHelper {
RwHelper(std::array<object_id_t, 4> rwIds) : rwIds(rwIds) {}
@ -10,17 +10,15 @@ struct RwHelper {
std::array<object_id_t, 4> rwIds = {};
};
class RwAssembly : public AssemblyBase {
class RwAssembly : public SharedPowerAssemblyBase {
public:
RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher,
RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switchId,
RwHelper helper);
private:
static constexpr uint8_t NUMBER_RWS = 4;
RwHelper helper;
PowerSwitcher switcher;
bool warningSwitch = true;
bool modeTransitionFailedSwitch = true;
FixedArrayList<ModeListEntry, NUMBER_RWS> modeTable;
ReturnValue_t initialize() override;
@ -35,12 +33,9 @@ class RwAssembly : public AssemblyBase {
bool isUseable(object_id_t object, Mode_t mode);
// AssemblyBase implementation
void performChildOperation() override;
ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override;
ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override;
ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override;
void startTransition(Mode_t mode, Submode_t submode) override;
void handleModeReached() override;
};
#endif /* MISSION_SYSTEM_RWASS_H_ */

View File

@ -3,4 +3,4 @@
#include "eive/objects.h"
RtdFdir::RtdFdir(object_id_t sensorId)
: DeviceHandlerFailureIsolation(sensorId, objects::TCS_BOARD_ASS) {}
: DeviceHandlerFailureIsolation(sensorId, objects::NO_OBJECT) {}

View File

@ -4,9 +4,11 @@
#include <fsfw/ipc/QueueFactory.h>
TcsBoardAssembly::TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher,
power::Switch_t theSwitch, TcsBoardHelper helper)
: AssemblyBase(objectId, 24), switcher(pwrSwitcher, theSwitch), helper(helper) {
eventQueue = QueueFactory::instance()->createMessageQueue(24);
power::Switch_t theSwitch, TcsBoardHelper helper,
std::atomic_bool& tcsShortlyUnavailable)
: SharedPowerAssemblyBase(objectId, pwrSwitcher, theSwitch, 16),
helper(helper),
tcsShortlyUnavailable(tcsShortlyUnavailable) {
ModeListEntry entry;
for (uint8_t idx = 0; idx < NUMBER_RTDS; idx++) {
entry.setObject(helper.rtdInfos[idx].first);
@ -16,23 +18,6 @@ TcsBoardAssembly::TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitc
}
}
void TcsBoardAssembly::performChildOperation() {
auto state = switcher.getState();
if (state != PowerSwitcher::WAIT_OFF and state != PowerSwitcher::WAIT_ON) {
AssemblyBase::performChildOperation();
return;
}
switcher.doStateMachine();
if (state == PowerSwitcher::WAIT_OFF and switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
// Indicator that a transition to off is finished
AssemblyBase::handleModeReached();
} else if (state == PowerSwitcher::WAIT_ON and
switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
// Indicator that mode commanding can be performed now
AssemblyBase::startTransition(targetMode, targetSubmode);
}
}
ReturnValue_t TcsBoardAssembly::commandChildren(Mode_t mode, Submode_t submode) {
ReturnValue_t result = returnvalue::OK;
// Initialize the mode table to ensure all devices are in a defined state
@ -50,6 +35,8 @@ ReturnValue_t TcsBoardAssembly::commandChildren(Mode_t mode, Submode_t submode)
if (mode == DeviceHandlerIF::MODE_NORMAL or mode == MODE_ON) {
result = handleNormalOrOnModeCmd(mode, submode);
}
} else {
tcsShortlyUnavailable = true;
}
HybridIterator<ModeListEntry> tableIter(modeTable.begin(), modeTable.end());
executeTable(tableIter);
@ -94,25 +81,6 @@ ReturnValue_t TcsBoardAssembly::isModeCombinationValid(Mode_t mode, Submode_t su
return HasModesIF::INVALID_MODE;
}
ReturnValue_t TcsBoardAssembly::initialize() { return AssemblyBase::initialize(); }
void TcsBoardAssembly::startTransition(Mode_t mode, Submode_t submode) {
if (mode != MODE_OFF) {
switcher.turnOn(true);
switcher.doStateMachine();
if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) {
AssemblyBase::startTransition(mode, submode);
} else {
// Need to wait with mode commanding until power switcher is done
targetMode = mode;
targetSubmode = submode;
}
} else {
// Perform regular mode commanding first
AssemblyBase::startTransition(mode, submode);
}
}
ReturnValue_t TcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode) {
ReturnValue_t result = returnvalue::OK;
bool needsSecondStep = false;
@ -169,21 +137,6 @@ bool TcsBoardAssembly::isUseable(object_id_t object, Mode_t mode) {
return false;
}
MessageQueueId_t TcsBoardAssembly::getEventReceptionQueue() { return eventQueue->getId(); }
void TcsBoardAssembly::handleModeReached() {
if (targetMode == MODE_OFF) {
switcher.turnOff(true);
switcher.doStateMachine();
// Need to wait with call to AssemblyBase::handleModeReached until power switcher is done
if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) {
AssemblyBase::handleModeReached();
}
} else {
AssemblyBase::handleModeReached();
}
}
void TcsBoardAssembly::handleChildrenLostMode(ReturnValue_t result) {
triggerEvent(CHILDREN_LOST_MODE, result);
startTransition(mode, submode);
@ -210,6 +163,12 @@ ReturnValue_t TcsBoardAssembly::checkAndHandleHealthStates(Mode_t deviceMode,
return status;
}
bool TcsBoardAssembly::checkAndHandleRecovery() {
bool recoveryPending = SharedPowerAssemblyBase::checkAndHandleRecovery();
tcsShortlyUnavailable = recoveryPending;
return recoveryPending;
}
void TcsBoardAssembly::handleModeTransitionFailed(ReturnValue_t result) {
if (targetMode == MODE_OFF) {
AssemblyBase::handleModeTransitionFailed(result);

View File

@ -4,6 +4,10 @@
#include <fsfw/container/FixedArrayList.h>
#include <fsfw/devicehandlers/AssemblyBase.h>
#include <fsfw/power/PowerSwitcher.h>
#include <mission/power/defs.h>
#include <mission/system/SharedPowerAssemblyBase.h>
#include <atomic>
#include "events/subsystemIdRanges.h"
#include "returnvalues/classIds.h"
@ -15,23 +19,20 @@ struct TcsBoardHelper {
std::array<std::pair<object_id_t, std::string>, 16> rtdInfos = {};
};
class TcsBoardAssembly : public AssemblyBase, public ConfirmsFailuresIF {
class TcsBoardAssembly : public SharedPowerAssemblyBase {
public:
static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::TCS_BOARD_ASS;
static constexpr Event CHILDREN_LOST_MODE = event::makeEvent(SUBSYSTEM_ID, 0, severity::MEDIUM);
TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher,
TcsBoardHelper helper);
ReturnValue_t initialize() override;
TcsBoardHelper helper, std::atomic_bool& tcsShortlyUnavailable);
private:
static constexpr uint8_t NUMBER_RTDS = 16;
PowerSwitcher switcher;
bool warningSwitch = true;
TcsBoardHelper helper;
FixedArrayList<ModeListEntry, NUMBER_RTDS> modeTable;
MessageQueueIF* eventQueue = nullptr;
std::atomic_bool& tcsShortlyUnavailable;
ReturnValue_t handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode);
/**
@ -42,17 +43,12 @@ class TcsBoardAssembly : public AssemblyBase, public ConfirmsFailuresIF {
*/
bool isUseable(object_id_t object, Mode_t mode);
// ConfirmFailureIF implementation
MessageQueueId_t getEventReceptionQueue() override;
// AssemblyBase implementation
void performChildOperation() override;
ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override;
ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override;
ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override;
void startTransition(Mode_t mode, Submode_t submode) override;
void handleModeReached() override;
ReturnValue_t checkAndHandleHealthStates(Mode_t deviceMode, Submode_t deviceSubmode);
bool checkAndHandleRecovery() override;
// These two overrides prevent a transition of the whole assembly back to off just because
// some devices are not working

9
mission/tcs/defs.h Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <atomic>
namespace tcs {
extern std::atomic_bool TCS_BOARD_SHORTLY_UNAVAILABLE;
}

View File

@ -338,6 +338,8 @@ ReturnValue_t PersistentTmStore::createMostRecentFile(std::optional<uint8_t> suf
ReturnValue_t PersistentTmStore::initializeTmStore() {
Clock::getClock_timeval(&currentTv);
updateBaseDir();
// Reset active file, base directory might have changed.
activeFile = std::nullopt;
return assignAndOrCreateMostRecentFile();
}

2
tmtc

Submodule tmtc updated: 9edbdf1a8d...01b3a894e6

View File

@ -15,6 +15,7 @@
TEST_CASE("Thermal Controller", "[ThermalController]") {
const object_id_t THERMAL_CONTROLLER_ID = 0x123;
std::atomic_bool tcsBrdShortlyUnavailable = false;
TemperatureSensorInserter::Max31865DummyMap map0;
TemperatureSensorInserter::Tmp1075DummyMap map1;
@ -29,7 +30,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") {
// testEnvironment::initialize();
ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler);
ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler, tcsBrdShortlyUnavailable);
ReturnValue_t result = controller.initialize();
REQUIRE(result == returnvalue::OK);