Merge branch 'develop' into acs-flp-safe

This commit is contained in:
Marius Eggert 2023-04-14 16:50:31 +02:00
commit a306b2a0af
55 changed files with 973 additions and 399 deletions

6
.gitmodules vendored
View File

@ -10,9 +10,6 @@
[submodule "thirdparty/lwgps"] [submodule "thirdparty/lwgps"]
path = thirdparty/lwgps path = thirdparty/lwgps
url = https://github.com/rmspacefish/lwgps.git url = https://github.com/rmspacefish/lwgps.git
[submodule "thirdparty/arcsec_star_tracker"]
path = thirdparty/arcsec_star_tracker
url = https://egit.irs.uni-stuttgart.de/eive/arcsec_star_tracker.git
[submodule "thirdparty/json"] [submodule "thirdparty/json"]
path = thirdparty/json path = thirdparty/json
url = https://github.com/nlohmann/json.git url = https://github.com/nlohmann/json.git
@ -22,3 +19,6 @@
[submodule "thirdparty/gomspace-sw"] [submodule "thirdparty/gomspace-sw"]
path = thirdparty/gomspace-sw path = thirdparty/gomspace-sw
url = https://egit.irs.uni-stuttgart.de/eive/gomspace-sw.git url = https://egit.irs.uni-stuttgart.de/eive/gomspace-sw.git
[submodule "thirdparty/sagittactl"]
path = thirdparty/sagittactl
url = https://egit.irs.uni-stuttgart.de/eive/sagittactl.git

View File

@ -16,14 +16,47 @@ will consitute of a breaking change warranting a new major release:
# [unreleased] # [unreleased]
- q7s-package: v2.5.0
- STR firmware was updated to v10.3
## Fixed
- Small fix for `install-obsw-yocto.sh` script
- Bugfix for STR firmware update procedure where the last remaining
bytes were not written properly.
- Bugfix for STR where an invalid reply was received for special requests
like firmware updates.
- Bugfix for shell command executors in command controller which lead to a crash.
## Changed
- STR `wire` library updated to v10.3. Submodule renamed to `sagittactl`.
## Added
- Add a way for the MAX31865 RTD handlers to recognize faulty/broken/off sensor devices.
- Add parameter interface for core controller
- Allow setting the preferred SD card via the new parameter interface of the core controller
with domain ID 0 and unque ID 0.
# [v1.44.1] 2023-04-12
- eive-tmtc: v2.22.1
## Fixed ## Fixed
- Bugfixes and improvements for SDC state machine. Internal state was not updated correctly due - 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. to a regression, so commanding the SDC state machine externally lead to confusing results.
- Heater states array in TCS controller was too small.
- Fixed a bug in persistent TM store, where the active file was not reset of SD card switches. - 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. 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 - Add a way for the SUS polling to detect broken or off devices by checking the retrieved
temperature for the all-ones value (0x0fff). 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 ## Changed
@ -31,14 +64,22 @@ will consitute of a breaking change warranting a new major release:
the active SD card is switched or there is a transition from hot redundant to cold redundant mode. 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 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. a way for them to perform any re-initialization tasks necessary after SD card switches.
- TCS controller now only has an OFF mode and an ON mode
## Changed - 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.
- Allow specifying custom OBSW update filename. This allowed keeping a cleaner file structure - Allow specifying custom OBSW update filename. This allowed keeping a cleaner file structure
where each update has a name including the version 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 files extracted during an update process are deleted after the update was performed to keep
the update directory cleaner. the update directory cleaner.
## Added
- TCS controller: SUBMODE_NO_HEATER_CTRL (1) added for ON mode. If this submode is
commanded, all heaters will be switched off and then no further heater
commanding will be done.
- 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.
# [v1.44.0] 2023-04-07 # [v1.44.0] 2023-04-07
- eive-tmtc: v2.22.0 - 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_MAJOR 1)
set(OBSW_VERSION_MINOR 44) set(OBSW_VERSION_MINOR 44)
set(OBSW_VERSION_REVISION 0) set(OBSW_VERSION_REVISION 1)
# set(CMAKE_VERBOSE TRUE) # set(CMAKE_VERBOSE TRUE)
@ -94,6 +94,9 @@ set(OBSW_ADD_SUN_SENSORS
set(OBSW_ADD_SUS_BOARD_ASS set(OBSW_ADD_SUS_BOARD_ASS
${INIT_VAL} ${INIT_VAL}
CACHE STRING "Add sun sensor board assembly") CACHE STRING "Add sun sensor board assembly")
set(OBSW_ADD_THERMAL_TEMP_INSERTER
${OBSW_Q7S_EM}
CACHE STRING "Add thermal sensor temperature inserter")
set(OBSW_ADD_ACS_BOARD set(OBSW_ADD_ACS_BOARD
${INIT_VAL} ${INIT_VAL}
CACHE STRING "Add ACS board module") CACHE STRING "Add ACS board module")
@ -220,7 +223,7 @@ set(LIB_EIVE_MISSION_PATH mission)
set(LIB_ETL_PATH ${THIRD_PARTY_FOLDER}/etl) set(LIB_ETL_PATH ${THIRD_PARTY_FOLDER}/etl)
set(LIB_CATCH2_PATH ${THIRD_PARTY_FOLDER}/Catch2) set(LIB_CATCH2_PATH ${THIRD_PARTY_FOLDER}/Catch2)
set(LIB_LWGPS_PATH ${THIRD_PARTY_FOLDER}/lwgps) set(LIB_LWGPS_PATH ${THIRD_PARTY_FOLDER}/lwgps)
set(LIB_ARCSEC_PATH ${THIRD_PARTY_FOLDER}/arcsec_star_tracker) set(LIB_ARCSEC_PATH ${THIRD_PARTY_FOLDER}/sagittactl)
set(LIB_JSON_PATH ${THIRD_PARTY_FOLDER}/json) set(LIB_JSON_PATH ${THIRD_PARTY_FOLDER}/json)
set(FSFW_WARNING_SHADOW_LOCAL_GCC OFF) set(FSFW_WARNING_SHADOW_LOCAL_GCC OFF)

View File

@ -151,7 +151,6 @@ void scheduling::initTasks() {
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
scheduling::printAddObjectError("Core controller dummy", objects::CORE_CONTROLLER); scheduling::printAddObjectError("Core controller dummy", objects::CORE_CONTROLLER);
} }
result = thermalTask->addComponent(objects::THERMAL_CONTROLLER); result = thermalTask->addComponent(objects::THERMAL_CONTROLLER);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
scheduling::printAddObjectError("THERMAL_CONTROLLER", objects::THERMAL_CONTROLLER); scheduling::printAddObjectError("THERMAL_CONTROLLER", objects::THERMAL_CONTROLLER);

View File

@ -43,6 +43,9 @@
#define OBSW_ADD_PL_PCDU @OBSW_ADD_PL_PCDU@ #define OBSW_ADD_PL_PCDU @OBSW_ADD_PL_PCDU@
#define OBSW_ADD_SYRLINKS @OBSW_ADD_SYRLINKS@ #define OBSW_ADD_SYRLINKS @OBSW_ADD_SYRLINKS@
#define OBSW_ADD_CCSDS_IP_CORES @OBSW_ADD_CCSDS_IP_CORES@ #define OBSW_ADD_CCSDS_IP_CORES @OBSW_ADD_CCSDS_IP_CORES@
// Only relevant for EM for TCS tests.
#define OBSW_ADD_THERMAL_TEMP_INSERTER @OBSW_ADD_THERMAL_TEMP_INSERTER@
// Set to 1 if all telemetry should be sent to the PTME IP Core // Set to 1 if all telemetry should be sent to the PTME IP Core
#define OBSW_TM_TO_PTME @OBSW_TM_TO_PTME@ #define OBSW_TM_TO_PTME @OBSW_TM_TO_PTME@
// Set to 1 if telecommands are received via the PDEC IP Core // Set to 1 if telecommands are received via the PDEC IP Core

View File

@ -9,7 +9,6 @@
#include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/serviceinterface/ServiceInterface.h"
#include "fsfw/timemanager/Stopwatch.h" #include "fsfw/timemanager/Stopwatch.h"
#include "fsfw/version.h" #include "fsfw/version.h"
#include "mission/sysDefs.h"
#include "watchdog/definitions.h" #include "watchdog/definitions.h"
#if OBSW_ADD_TMTC_UDP_SERVER == 1 #if OBSW_ADD_TMTC_UDP_SERVER == 1
#include "fsfw/osal/common/UdpTmTcBridge.h" #include "fsfw/osal/common/UdpTmTcBridge.h"
@ -40,7 +39,8 @@ CoreController::CoreController(object_id_t objectId, bool enableHkSet)
cmdRepliesSizes(128), cmdRepliesSizes(128),
opDivider5(5), opDivider5(5),
opDivider10(10), opDivider10(10),
hkSet(this) { hkSet(this),
paramHelper(this) {
cmdExecutor.setRingBuffer(&cmdReplyBuf, &cmdRepliesSizes); cmdExecutor.setRingBuffer(&cmdReplyBuf, &cmdRepliesSizes);
try { try {
sdcMan = SdCardManager::instance(); sdcMan = SdCardManager::instance();
@ -88,6 +88,10 @@ CoreController::CoreController(object_id_t objectId, bool enableHkSet)
CoreController::~CoreController() {} CoreController::~CoreController() {}
ReturnValue_t CoreController::handleCommandMessage(CommandMessage *message) { ReturnValue_t CoreController::handleCommandMessage(CommandMessage *message) {
ReturnValue_t result = paramHelper.handleParameterMessage(message);
if (result == returnvalue::OK) {
return result;
}
return ExtendedControllerBase::handleCommandMessage(message); return ExtendedControllerBase::handleCommandMessage(message);
} }
@ -113,7 +117,7 @@ void CoreController::performControlOperation() {
bool replyReceived = false; bool replyReceived = false;
// TODO: We could read the data in the ring buffer and send it as an action data reply. // TODO: We could read the data in the ring buffer and send it as an action data reply.
if (cmdExecutor.check(replyReceived) == CommandExecutor::EXECUTION_FINISHED) { if (cmdExecutor.check(replyReceived) == CommandExecutor::EXECUTION_FINISHED) {
actionHelper.finish(true, successRecipient, core::EXECUTE_SHELL_CMD); actionHelper.finish(true, successRecipient, core::EXECUTE_SHELL_CMD_BLOCKING);
shellCmdIsExecuting = false; shellCmdIsExecuting = false;
cmdReplyBuf.clear(); cmdReplyBuf.clear();
while (not cmdRepliesSizes.empty()) { while (not cmdRepliesSizes.empty()) {
@ -154,6 +158,11 @@ ReturnValue_t CoreController::initialize() {
<< std::endl; << std::endl;
} }
result = paramHelper.initialize();
if (result != returnvalue::OK) {
return result;
}
sdStateMachine(); sdStateMachine();
triggerEvent(core::REBOOT_SW, CURRENT_CHIP, CURRENT_COPY); triggerEvent(core::REBOOT_SW, CURRENT_CHIP, CURRENT_COPY);
@ -294,6 +303,41 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
// Completion will be reported by SD card state machine // Completion will be reported by SD card state machine
return returnvalue::OK; return returnvalue::OK;
} }
case (SYSTEMCTL_CMD_EXECUTOR): {
// Expect one byte systemctl command type and a unit name with at least one byte as minimum.
if (size < 2) {
return HasActionsIF::INVALID_PARAMETERS;
}
if (data[0] >= core::SystemctlCmd::NUM_CMDS) {
return HasActionsIF::INVALID_PARAMETERS;
}
core::SystemctlCmd cmdType = static_cast<core::SystemctlCmd>(data[0]);
std::string unitName = std::string(reinterpret_cast<const char *>(data + 1), size - 1);
std::ostringstream oss("systemctl ", std::ostringstream::ate);
switch (cmdType) {
case (core::SystemctlCmd::START): {
oss << "start ";
break;
}
case (core::SystemctlCmd::STOP): {
oss << "stop ";
break;
}
case (core::SystemctlCmd::RESTART): {
oss << "restart ";
break;
}
default: {
return HasActionsIF::INVALID_PARAMETERS;
}
}
oss << unitName;
int result = std::system(oss.str().c_str());
if (result != 0) {
return returnvalue::FAILED;
}
return EXECUTION_FINISHED;
}
case (SWITCH_IMG_LOCK): { case (SWITCH_IMG_LOCK): {
if (size != 3) { if (size != 3) {
return HasActionsIF::INVALID_PARAMETERS; return HasActionsIF::INVALID_PARAMETERS;
@ -324,13 +368,22 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_
// Warning: This function will never return, because it reboots the system // Warning: This function will never return, because it reboots the system
return actionReboot(data, size); return actionReboot(data, size);
} }
case (EXECUTE_SHELL_CMD): { case (EXECUTE_SHELL_CMD_BLOCKING): {
std::string cmd = std::string(cmd, size); std::string cmdToExecute = std::string(reinterpret_cast<const char*>(data), size);
int result = std::system(cmdToExecute.c_str());
if (result != 0) {
// TODO: Data reply with returnalue maybe?
return returnvalue::FAILED;
}
return EXECUTION_FINISHED;
}
case (EXECUTE_SHELL_CMD_NON_BLOCKING): {
std::string cmdToExecute = std::string(reinterpret_cast<const char*>(data), size);
if (cmdExecutor.getCurrentState() == CommandExecutor::States::PENDING or if (cmdExecutor.getCurrentState() == CommandExecutor::States::PENDING or
shellCmdIsExecuting) { shellCmdIsExecuting) {
return HasActionsIF::IS_BUSY; return HasActionsIF::IS_BUSY;
} }
cmdExecutor.load(cmd, false, false); cmdExecutor.load(cmdToExecute, false, false);
ReturnValue_t result = cmdExecutor.execute(); ReturnValue_t result = cmdExecutor.execute();
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
return result; return result;
@ -1298,7 +1351,7 @@ void CoreController::performMountedSdCardOperations() {
auto mountedSdCardOp = [&](sd::SdCard sdCard, std::string mntPoint) { auto mountedSdCardOp = [&](sd::SdCard sdCard, std::string mntPoint) {
if (not performOneShotSdCardOpsSwitch) { if (not performOneShotSdCardOpsSwitch) {
std::ostringstream path; std::ostringstream path;
path << mntPoint << "/" << CONF_FOLDER; path << mntPoint << "/" << core::CONF_FOLDER;
std::error_code e; std::error_code e;
if (not std::filesystem::exists(path.str()), e) { if (not std::filesystem::exists(path.str()), e) {
bool created = std::filesystem::create_directory(path.str(), e); bool created = std::filesystem::create_directory(path.str(), e);
@ -2124,7 +2177,38 @@ void CoreController::announceBootCounts() {
totalBootCount & 0xffffffff); totalBootCount & 0xffffffff);
} }
MessageQueueId_t CoreController::getCommandQueue() const {
return ExtendedControllerBase::getCommandQueue();
}
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();
} }
ReturnValue_t CoreController::getParameter(uint8_t domainId, uint8_t uniqueIdentifier,
ParameterWrapper *parameterWrapper,
const ParameterWrapper *newValues,
uint16_t startAtIndex) {
if (domainId != 0) {
return HasParametersIF::INVALID_DOMAIN_ID;
}
if (uniqueIdentifier >= ParamId::NUM_IDS) {
return HasParametersIF::INVALID_IDENTIFIER_ID;
}
uint8_t newPrefSd;
ReturnValue_t result = newValues->getElement(&newPrefSd);
if (result != returnvalue::OK) {
return result;
}
// Only SD card 0 (0) and 1 (1) are allowed values.
if (newPrefSd > 1) {
return HasParametersIF::INVALID_VALUE;
}
result = sdcMan->setPreferredSdCard(static_cast<sd::SdCard>(newPrefSd));
if (result != returnvalue::OK) {
return returnvalue::FAILED;
}
parameterWrapper->set(prefSdRaw);
return returnvalue::OK;
}

View File

@ -4,6 +4,8 @@
#include <fsfw/container/DynamicFIFO.h> #include <fsfw/container/DynamicFIFO.h>
#include <fsfw/container/SimpleRingBuffer.h> #include <fsfw/container/SimpleRingBuffer.h>
#include <fsfw/globalfunctions/PeriodicOperationDivider.h> #include <fsfw/globalfunctions/PeriodicOperationDivider.h>
#include <fsfw/parameters/ParameterHelper.h>
#include <fsfw/parameters/ReceivesParameterMessagesIF.h>
#include <libxiphos.h> #include <libxiphos.h>
#include <mission/acs/archive/GPSDefinitions.h> #include <mission/acs/archive/GPSDefinitions.h>
#include <mission/utility/trace.h> #include <mission/utility/trace.h>
@ -16,17 +18,11 @@
#include "bsp_q7s/fs/SdCardManager.h" #include "bsp_q7s/fs/SdCardManager.h"
#include "events/subsystemIdRanges.h" #include "events/subsystemIdRanges.h"
#include "fsfw/controller/ExtendedControllerBase.h" #include "fsfw/controller/ExtendedControllerBase.h"
#include "mission/sysDefs.h"
class Timer; class Timer;
class SdCardManager; class SdCardManager;
namespace xsc {
enum Chip : int { CHIP_0, CHIP_1, NO_CHIP, SELF_CHIP, ALL_CHIP };
enum Copy : int { COPY_0, COPY_1, NO_COPY, SELF_COPY, ALL_COPY };
} // namespace xsc
struct RebootFile { struct RebootFile {
static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10; static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10;
@ -48,8 +44,10 @@ struct RebootFile {
xsc::Copy mechanismNextCopy = xsc::Copy::NO_COPY; xsc::Copy mechanismNextCopy = xsc::Copy::NO_COPY;
}; };
class CoreController : public ExtendedControllerBase { class CoreController : public ExtendedControllerBase, public ReceivesParameterMessagesIF {
public: public:
enum ParamId : uint8_t { PREF_SD = 0, NUM_IDS };
static xsc::Chip CURRENT_CHIP; static xsc::Chip CURRENT_CHIP;
static xsc::Copy CURRENT_COPY; static xsc::Copy CURRENT_COPY;
@ -57,18 +55,12 @@ class CoreController : public ExtendedControllerBase {
static constexpr char CHIP_STATE_FILE[] = "/tmp/chip_prot_status.txt"; static constexpr char CHIP_STATE_FILE[] = "/tmp/chip_prot_status.txt";
static constexpr char CURR_COPY_FILE[] = "/tmp/curr_copy.txt"; static constexpr char CURR_COPY_FILE[] = "/tmp/curr_copy.txt";
static constexpr char CONF_FOLDER[] = "conf";
static constexpr char VERSION_FILE_NAME[] = "version.txt";
static constexpr char REBOOT_FILE_NAME[] = "reboot.txt";
static constexpr char TIME_FILE_NAME[] = "time_backup.txt";
const std::string VERSION_FILE = const std::string VERSION_FILE =
"/" + std::string(CONF_FOLDER) + "/" + std::string(VERSION_FILE_NAME); "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::VERSION_FILE_NAME);
const std::string REBOOT_FILE = const std::string REBOOT_FILE =
"/" + std::string(CONF_FOLDER) + "/" + std::string(REBOOT_FILE_NAME); "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::REBOOT_FILE_NAME);
const std::string BACKUP_TIME_FILE = const std::string BACKUP_TIME_FILE =
"/" + std::string(CONF_FOLDER) + "/" + std::string(TIME_FILE_NAME); "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::TIME_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";
@ -152,6 +144,7 @@ class CoreController : public ExtendedControllerBase {
SdCardManager* sdcMan = nullptr; SdCardManager* sdcMan = nullptr;
MessageQueueIF* eventQueue = nullptr; MessageQueueIF* eventQueue = nullptr;
uint8_t prefSdRaw = sd::SdCard::SLOT_0;
SdStates sdFsmState = SdStates::START; SdStates sdFsmState = SdStates::START;
SdStates fsmStateAfterDelay = SdStates::IDLE; SdStates fsmStateAfterDelay = SdStates::IDLE;
enum SdCfgMode { PASSIVE, COLD_REDUNDANT, HOT_REDUNDANT }; enum SdCfgMode { PASSIVE, COLD_REDUNDANT, HOT_REDUNDANT };
@ -216,10 +209,16 @@ class CoreController : public ExtendedControllerBase {
core::HkSet hkSet; core::HkSet hkSet;
ParameterHelper paramHelper;
#if OBSW_SD_CARD_MUST_BE_ON == 1 #if OBSW_SD_CARD_MUST_BE_ON == 1
bool remountAttemptFlag = true; bool remountAttemptFlag = true;
#endif #endif
MessageQueueId_t getCommandQueue() const override;
ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueIdentifier,
ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues,
uint16_t startAtIndex) override;
ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) override; LocalDataPoolManager& poolManager) override;

View File

@ -302,6 +302,9 @@ void scheduling::initTasks() {
PeriodicTaskIF* tcsSystemTask = factory->createPeriodicTask( PeriodicTaskIF* tcsSystemTask = factory->createPeriodicTask(
"TCS_TASK", 50, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.5, missedDeadlineFunc, &RR_SCHEDULING); "TCS_TASK", 50, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.5, missedDeadlineFunc, &RR_SCHEDULING);
#if OBSW_ADD_THERMAL_TEMP_INSERTER == 1
tcsSystemTask->addComponent(objects::THERMAL_TEMP_INSERTER);
#endif
scheduling::scheduleRtdSensors(tcsSystemTask); scheduling::scheduleRtdSensors(tcsSystemTask);
result = tcsSystemTask->addComponent(objects::TCS_SUBSYSTEM); result = tcsSystemTask->addComponent(objects::TCS_SUBSYSTEM);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
@ -317,12 +320,10 @@ void scheduling::initTasks() {
scheduling::printAddObjectError("THERMAL_CONTROLLER", objects::THERMAL_CONTROLLER); scheduling::printAddObjectError("THERMAL_CONTROLLER", objects::THERMAL_CONTROLLER);
} }
#endif #endif
#if OBSW_ADD_HEATERS == 1
result = tcsSystemTask->addComponent(objects::HEATER_HANDLER); result = tcsSystemTask->addComponent(objects::HEATER_HANDLER);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER); scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER);
} }
#endif
#if OBSW_ADD_SYRLINKS == 1 #if OBSW_ADD_SYRLINKS == 1
PeriodicTaskIF* syrlinksCom = factory->createPeriodicTask( PeriodicTaskIF* syrlinksCom = factory->createPeriodicTask(

View File

@ -1,5 +1,6 @@
#include "TemperatureSensorInserter.h" #include "TemperatureSensorInserter.h"
#include <fsfw/datapool/PoolReadGuard.h>
#include <objects/systemObjectList.h> #include <objects/systemObjectList.h>
#include <cmath> #include <cmath>
@ -14,10 +15,7 @@ TemperatureSensorInserter::TemperatureSensorInserter(object_id_t objectId,
tmp1075DummyMap(std::move(tempTmpSensorDummies_)) {} tmp1075DummyMap(std::move(tempTmpSensorDummies_)) {}
ReturnValue_t TemperatureSensorInserter::initialize() { ReturnValue_t TemperatureSensorInserter::initialize() {
if (performTest) { testCase = TestCase::COLD_STR_CONSECUTIVE;
if (testCase == TestCase::COOL_SYRLINKS) {
}
}
return returnvalue::OK; return returnvalue::OK;
} }
@ -33,35 +31,72 @@ ReturnValue_t TemperatureSensorInserter::performOperation(uint8_t opCode) {
tempsWereInitialized = true; tempsWereInitialized = true;
} }
if (cycles == 10) { switch (testCase) {
max31865DummyMap[objects::RTD_9_IC12_HPA]->setTemperature(-100, true); case (TestCase::NONE): {
max31865DummyMap[objects::RTD_11_IC14_MPA]->setTemperature(-100, true); break;
}
case (TestCase::COLD_SYRLINKS): {
// TODO: How do I insert this?
// Does not work on EM, where a real syrlinks device is connected.
if (cycles == 15) {
lp_var_t<float> tempSyrlinksBasebandBoard =
lp_var_t<float>(objects::SYRLINKS_HANDLER, syrlinks::TEMP_BASEBAND_BOARD);
PoolReadGuard pg(&tempSyrlinksBasebandBoard);
tempSyrlinksBasebandBoard.value = -50;
}
if (cycles == 30) {
lp_var_t<float> tempSyrlinksBasebandBoard =
lp_var_t<float>(objects::SYRLINKS_HANDLER, syrlinks::TEMP_BASEBAND_BOARD);
PoolReadGuard pg(&tempSyrlinksBasebandBoard);
tempSyrlinksBasebandBoard.value = 0;
}
break;
}
case (TestCase::COLD_HPA): {
if (cycles == 15) {
sif::debug << "Setting cold HPA temperature" << std::endl;
max31865DummyMap[objects::RTD_9_IC12_HPA]->setTemperature(-60, true);
}
if (cycles == 30) {
sif::debug << "Setting HPA temperature back to normal" << std::endl;
max31865DummyMap[objects::RTD_9_IC12_HPA]->setTemperature(0, true);
}
break;
}
case (TestCase::COLD_MGT): {
if (cycles == 15) {
sif::debug << "Setting cold MGT temperature" << std::endl;
max31865DummyMap[objects::RTD_15_IC18_IMTQ]->setTemperature(-60, true);
}
if (cycles == 30) {
sif::debug << "Setting MGT temperature back to normal" << std::endl;
max31865DummyMap[objects::RTD_15_IC18_IMTQ]->setTemperature(0, true);
}
break;
}
case (TestCase::COLD_STR):
case (TestCase::COLD_STR_CONSECUTIVE): {
if (cycles == 15) {
sif::debug << "Setting cold STR temperature" << std::endl;
max31865DummyMap[objects::RTD_4_IC7_STARTRACKER]->setTemperature(-40, true);
}
if (cycles == 30) {
sif::debug << "Setting STR temperature back to normal" << std::endl;
max31865DummyMap[objects::RTD_4_IC7_STARTRACKER]->setTemperature(0, true);
}
if (testCase == TestCase::COLD_STR_CONSECUTIVE) {
if (cycles == 45) {
sif::debug << "Setting cold STR temperature again" << std::endl;
max31865DummyMap[objects::RTD_4_IC7_STARTRACKER]->setTemperature(-40, true);
}
if (cycles == 60) {
sif::debug << "Setting STR temperature back to normal again" << std::endl;
max31865DummyMap[objects::RTD_4_IC7_STARTRACKER]->setTemperature(0, true);
}
}
break;
}
} }
if (cycles == 35) {
max31865DummyMap[objects::RTD_9_IC12_HPA]->setTemperature(0, true);
max31865DummyMap[objects::RTD_11_IC14_MPA]->setTemperature(0, true);
max31865DummyMap[objects::RTD_2_IC5_4K_CAMERA]->setTemperature(-100, true);
}
if (cycles == 60) {
max31865DummyMap[objects::RTD_9_IC12_HPA]->setTemperature(-100, true);
max31865DummyMap[objects::RTD_11_IC14_MPA]->setTemperature(0, true);
}
/*
ReturnValue_t result = max31865PlocHeatspreaderSet.read();
if (result != returnvalue::OK) {
sif::warning << "Failed to read temperature from MAX31865 dataset" << std::endl;
}
max31865PlocHeatspreaderSet.rtdValue = value - 5;
max31865PlocHeatspreaderSet.temperatureCelcius = value;
if ((iteration % 100) < 20) {
max31865PlocHeatspreaderSet.setValidity(false, true);
} else {
max31865PlocHeatspreaderSet.setValidity(true, true);
}
max31865PlocHeatspreaderSet.commit();
*/
cycles++; cycles++;
return returnvalue::OK; return returnvalue::OK;
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <fsfw/controller/ExtendedControllerBase.h> #include <fsfw/controller/ExtendedControllerBase.h>
#include <mission/com/syrlinksDefs.h>
#include <mission/tcs/Max31865Definitions.h> #include <mission/tcs/Max31865Definitions.h>
#include "Max31865Dummy.h" #include "Max31865Dummy.h"
@ -22,11 +23,18 @@ class TemperatureSensorInserter : public ExecutableObjectIF, public SystemObject
private: private:
Max31865DummyMap max31865DummyMap; Max31865DummyMap max31865DummyMap;
Tmp1075DummyMap tmp1075DummyMap; Tmp1075DummyMap tmp1075DummyMap;
enum TestCase { NONE = 0, COOL_SYRLINKS = 1 };
enum TestCase {
NONE = 0,
COLD_SYRLINKS = 1,
COLD_HPA = 2,
COLD_MGT = 3,
COLD_STR = 4,
COLD_STR_CONSECUTIVE = 5,
};
int iteration = 0; int iteration = 0;
uint32_t cycles = 0; uint32_t cycles = 0;
bool tempsWereInitialized = false; bool tempsWereInitialized = false;
bool performTest = false;
TestCase testCase = TestCase::NONE; TestCase testCase = TestCase::NONE;
// void noise(); // void noise();

View File

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

2
fsfw

@ -1 +1 @@
Subproject commit 285d327b97514946f0714e477289f67ee8bd413f Subproject commit ffa2fa477f105cc876264335d5b25fc9b174a181

View File

@ -29,6 +29,7 @@
#include "mission/system/acs/acsModeTree.h" #include "mission/system/acs/acsModeTree.h"
#include "mission/system/tree/payloadModeTree.h" #include "mission/system/tree/payloadModeTree.h"
#include "mission/system/tree/tcsModeTree.h" #include "mission/system/tree/tcsModeTree.h"
#include "mission/tcs/defs.h"
void ObjectFactory::createSunSensorComponents(GpioIF* gpioComIF, SpiComIF* spiComIF, void ObjectFactory::createSunSensorComponents(GpioIF* gpioComIF, SpiComIF* spiComIF,
PowerSwitchIF& pwrSwitcher, std::string spiDev, PowerSwitchIF& pwrSwitcher, std::string spiDev,
@ -278,7 +279,8 @@ void ObjectFactory::createRtdComponents(std::string spiDev, GpioIF* gpioComIF,
std::array<Max31865EiveHandler*, NUM_RTDS> rtds = {}; std::array<Max31865EiveHandler*, NUM_RTDS> rtds = {};
RtdFdir* rtdFdir = nullptr; 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 // Create special low level reader communication interface
new Max31865RtdPolling(objects::SPI_RTD_COM_IF, comIF, gpioComIF); 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 != adis.mode) {
if (req->mode == acs::SimpleSensorMode::NORMAL) { if (req->mode == acs::SimpleSensorMode::NORMAL) {
adis.type = req->type; 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) { if (adis.type == adis1650x::Type::ADIS16507) {
adis.ownReply.data.accelScaling = adis1650x::ACCELEROMETER_RANGE_16507; adis.ownReply.data.accelScaling = adis1650x::ACCELEROMETER_RANGE_16507;
} else if (adis.type == adis1650x::Type::ADIS16505) { } 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.cfgWasSet = false;
adis.ownReply.dataWasSet = false; adis.ownReply.dataWasSet = false;
} }
adis.replyResult = returnvalue::FAILED;
adis.mode = req->mode; adis.mode = req->mode;
} }
return returnvalue::OK; return returnvalue::OK;
@ -145,6 +147,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
} else { } else {
gyro.ownReply.cfgWasSet = false; gyro.ownReply.cfgWasSet = false;
} }
gyro.replyResult = returnvalue::FAILED;
gyro.mode = req->mode; gyro.mode = req->mode;
} }
return returnvalue::OK; return returnvalue::OK;
@ -163,6 +166,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
mgm.ownReply.dataWasSet = false; mgm.ownReply.dataWasSet = false;
mgm.ownReply.temperatureWasSet = false; mgm.ownReply.temperatureWasSet = false;
} }
mgm.replyResult = returnvalue::FAILED;
mgm.mode = req->mode; mgm.mode = req->mode;
} }
return returnvalue::OK; return returnvalue::OK;
@ -180,6 +184,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send
} else { } else {
mgm.ownReply.dataWasRead = false; mgm.ownReply.dataWasRead = false;
} }
mgm.replyResult = returnvalue::FAILED;
mgm.mode = req->mode; mgm.mode = req->mode;
} }
return returnvalue::OK; return returnvalue::OK;
@ -309,18 +314,18 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) {
std::memcpy(cmdBuf.data() + 1, l3g.sensorCfg, 5); std::memcpy(cmdBuf.data() + 1, l3g.sensorCfg, 5);
result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6); result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
l3g.replyResult = returnvalue::OK; l3g.replyResult = result;
} }
// Ignore useless reply and red config // Ignore useless reply and red config
cmdBuf[0] = l3gd20h::CTRL_REG_1 | l3gd20h::AUTO_INCREMENT_MASK | l3gd20h::READ_MASK; cmdBuf[0] = l3gd20h::CTRL_REG_1 | l3gd20h::AUTO_INCREMENT_MASK | l3gd20h::READ_MASK;
std::memset(cmdBuf.data() + 1, 0, 5); std::memset(cmdBuf.data() + 1, 0, 5);
result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6); result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
l3g.replyResult = returnvalue::OK; l3g.replyResult = result;
} }
result = spiComIF.readReceivedMessage(l3g.cookie, &rawReply, &dummy); result = spiComIF.readReceivedMessage(l3g.cookie, &rawReply, &dummy);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
l3g.replyResult = returnvalue::OK; l3g.replyResult = result;
} }
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
// Cross check configuration as verification that communication is working // Cross check configuration as verification that communication is working
@ -331,6 +336,7 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) {
return; return;
} }
} }
l3g.replyResult = returnvalue::OK;
l3g.performStartup = false; l3g.performStartup = false;
l3g.ownReply.cfgWasSet = true; l3g.ownReply.cfgWasSet = true;
l3g.ownReply.sensitivity = l3gd20h::ctrlReg4ToSensitivity(l3g.sensorCfg[3]); l3g.ownReply.sensitivity = l3gd20h::ctrlReg4ToSensitivity(l3g.sensorCfg[3]);
@ -357,6 +363,7 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) {
return; return;
} }
} }
l3g.replyResult = returnvalue::OK;
l3g.ownReply.statusReg = rawReply[l3gd20h::STATUS_IDX]; 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[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]; 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) { void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) {
ReturnValue_t result; ReturnValue_t result;
acs::SimpleSensorMode mode = acs::SimpleSensorMode::OFF; acs::SimpleSensorMode mode = acs::SimpleSensorMode::OFF;
bool cdHasTimedOut = false;
bool mustPerformStartup = false; bool mustPerformStartup = false;
{ {
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
mode = gyro.mode; mode = gyro.mode;
cdHasTimedOut = gyro.countdown.hasTimedOut();
mustPerformStartup = gyro.performStartup; mustPerformStartup = gyro.performStartup;
} }
if (mode == acs::SimpleSensorMode::OFF) { if (mode == acs::SimpleSensorMode::OFF) {
return; return;
} }
if (not cdHasTimedOut) {
return;
}
if (mustPerformStartup) { if (mustPerformStartup) {
uint8_t regList[6]; uint8_t regList[6];
// Read configuration // Read configuration
@ -495,6 +497,7 @@ void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) {
gyro.ownReply.cfg.prodId = prodId; gyro.ownReply.cfg.prodId = prodId;
gyro.ownReply.data.sensitivity = adis1650x::rangMdlToSensitivity(gyro.ownReply.cfg.rangMdl); gyro.ownReply.data.sensitivity = adis1650x::rangMdlToSensitivity(gyro.ownReply.cfg.rangMdl);
gyro.performStartup = false; gyro.performStartup = false;
gyro.replyResult = returnvalue::OK;
} }
// Read regular registers // Read regular registers
std::memcpy(cmdBuf.data(), adis1650x::BURST_READ_ENABLE.data(), 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); MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
gyro.replyResult = returnvalue::OK;
gyro.ownReply.dataWasSet = true; gyro.ownReply.dataWasSet = true;
gyro.ownReply.cfg.diagStat = rawReply[2] << 8 | rawReply[3]; gyro.ownReply.cfg.diagStat = rawReply[2] << 8 | rawReply[3];
gyro.ownReply.data.angVelocities[0] = (rawReply[4] << 8) | rawReply[5]; 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 // Done here. We can always read back config and data during periodic handling
mgm.performStartup = false; mgm.performStartup = false;
mgm.replyResult = returnvalue::OK;
} }
cmdBuf[0] = mgmLis3::readCommand(mgmLis3::CTRL_REG1, true); cmdBuf[0] = mgmLis3::readCommand(mgmLis3::CTRL_REG1, true);
std::memset(cmdBuf.data() + 1, 0, mgmLis3::NR_OF_DATA_AND_CFG_REGISTERS); 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 // 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 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]) { rawReply[4] != mgm.cfg[3] or rawReply[5] != mgm.cfg[4]) {
mgm.replyResult = result; mgm.replyResult = returnvalue::FAILED;
return; return;
} }
{ {
@ -634,6 +639,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) {
return; return;
} }
MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
mgm.replyResult = returnvalue::OK;
mgm.ownReply.temperatureWasSet = true; mgm.ownReply.temperatureWasSet = true;
mgm.ownReply.temperatureRaw = (rawReply[2] << 8) | rawReply[1]; mgm.ownReply.temperatureRaw = (rawReply[2] << 8) | rawReply[1];
} }
@ -704,6 +710,7 @@ void AcsBoardPolling::mgmRm3100Handler(MgmRm3100& mgm) {
return; return;
} }
mgm.performStartup = false; mgm.performStartup = false;
mgm.replyResult = returnvalue::OK;
} }
// Regular read operation // Regular read operation
cmdBuf[0] = mgmRm3100::MEASUREMENT_REG_START | mgmRm3100::READ_MASK; 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.scaleFactors[idx] = 1.0 / mgmRm3100::DEFAULT_GAIN;
} }
mgm.ownReply.dataWasRead = true; mgm.ownReply.dataWasRead = true;
mgm.replyResult = returnvalue::OK;
// Bitshift trickery to account for 24 bit signed value. // Bitshift trickery to account for 24 bit signed value.
mgm.ownReply.mgmValuesRaw[0] = mgm.ownReply.mgmValuesRaw[0] =
((rawReply[1] << 24) | (rawReply[2] << 16) | (rawReply[3] << 8)) >> 8; ((rawReply[1] << 24) | (rawReply[2] << 16) | (rawReply[3] << 8)) >> 8;

View File

@ -1,6 +1,10 @@
target_sources( target_sources(${OBSW_NAME} PUBLIC AcsBoardPolling.cpp ImtqPollingTask.cpp
${OBSW_NAME} PUBLIC AcsBoardPolling.cpp ImtqPollingTask.cpp RwPollingTask.cpp RwPollingTask.cpp SusPolling.cpp)
SusPolling.cpp StrComHandler.cpp)
# Dependency on proprietary library
if(TGT_BSP MATCHES "arm/q7s")
target_sources(${OBSW_NAME} PUBLIC StrComHandler.cpp)
endif()
if(EIVE_BUILD_GPSD_GPS_HANDLER) if(EIVE_BUILD_GPSD_GPS_HANDLER)
target_sources(${OBSW_NAME} PRIVATE GpsHyperionLinuxController.cpp) target_sources(${OBSW_NAME} PRIVATE GpsHyperionLinuxController.cpp)

View File

@ -89,7 +89,7 @@ ReturnValue_t StrComHandler::performOperation(uint8_t operationCode) {
break; break;
} }
case InternalState::FIRMWARE_UPDATE: { case InternalState::FIRMWARE_UPDATE: {
replyTimeout.setTimeout(200); replyTimeout.setTimeout(2000);
resetReplyHandlingState(); resetReplyHandlingState();
result = performFirmwareUpdate(); result = performFirmwareUpdate();
if (result == returnvalue::OK) { if (result == returnvalue::OK) {
@ -125,6 +125,7 @@ ReturnValue_t StrComHandler::startImageUpload(std::string fullname) {
} }
{ {
MutexGuard mg(lock); MutexGuard mg(lock);
replyWasReceived = false;
state = InternalState::UPLOAD_IMAGE; state = InternalState::UPLOAD_IMAGE;
} }
semaphore.release(); semaphore.release();
@ -151,6 +152,7 @@ ReturnValue_t StrComHandler::startImageDownload(std::string path) {
downloadImage.path = path; downloadImage.path = path;
{ {
MutexGuard mg(lock); MutexGuard mg(lock);
replyWasReceived = false;
state = InternalState::DOWNLOAD_IMAGE; state = InternalState::DOWNLOAD_IMAGE;
} }
terminate = false; terminate = false;
@ -187,6 +189,7 @@ ReturnValue_t StrComHandler::startFirmwareUpdate(std::string fullname) {
flashWrite.lastRegion = static_cast<uint8_t>(startracker::FirmwareRegions::LAST); flashWrite.lastRegion = static_cast<uint8_t>(startracker::FirmwareRegions::LAST);
{ {
MutexGuard mg(lock); MutexGuard mg(lock);
replyWasReceived = false;
state = InternalState::FIRMWARE_UPDATE; state = InternalState::FIRMWARE_UPDATE;
} }
semaphore.release(); semaphore.release();
@ -216,6 +219,7 @@ ReturnValue_t StrComHandler::startFlashRead(std::string path, uint8_t startRegio
flashRead.size = length; flashRead.size = length;
{ {
MutexGuard mg(lock); MutexGuard mg(lock);
replyWasReceived = false;
state = InternalState::FLASH_READ; state = InternalState::FLASH_READ;
} }
semaphore.release(); semaphore.release();
@ -390,7 +394,8 @@ ReturnValue_t StrComHandler::performFlashWrite() {
#endif #endif
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
uint32_t size = 0; uint32_t size = 0;
uint32_t bytesWritten = 0; uint32_t bytesWrittenInRegion = 0;
size_t totalBytesWritten = 0;
uint32_t fileSize = 0; uint32_t fileSize = 0;
struct WriteActionRequest req; struct WriteActionRequest req;
@ -412,20 +417,18 @@ ReturnValue_t StrComHandler::performFlashWrite() {
ProgressPrinter progressPrinter("Flash write", fileSize); ProgressPrinter progressPrinter("Flash write", fileSize);
#endif /* OBSW_DEBUG_STARTRACKER == 1 */ #endif /* OBSW_DEBUG_STARTRACKER == 1 */
uint32_t fileChunks = fileSize / CHUNK_SIZE; uint32_t fileChunks = fileSize / CHUNK_SIZE;
bytesWritten = 0; bytesWrittenInRegion = 0;
req.region = flashWrite.firstRegion; req.region = flashWrite.firstRegion;
req.length = CHUNK_SIZE; req.length = CHUNK_SIZE;
for (uint32_t idx = 0; idx < fileChunks; idx++) {
if (terminate) { auto writeNextSegment = [&](uint32_t chunkIdx) {
return returnvalue::OK; file.seekg(chunkIdx * CHUNK_SIZE, file.beg);
}
file.seekg(idx * CHUNK_SIZE, file.beg);
file.read(reinterpret_cast<char*>(req.data), CHUNK_SIZE); file.read(reinterpret_cast<char*>(req.data), CHUNK_SIZE);
if (bytesWritten + CHUNK_SIZE > FLASH_REGION_SIZE) { if (bytesWrittenInRegion + CHUNK_SIZE > FLASH_REGION_SIZE) {
req.region++; req.region++;
bytesWritten = 0; bytesWrittenInRegion = 0;
} }
req.address = bytesWritten; req.address = bytesWrittenInRegion;
arc_pack_write_action_req(&req, cmdBuf.data(), &size); arc_pack_write_action_req(&req, cmdBuf.data(), &size);
result = sendAndRead(size, req.address); result = sendAndRead(size, req.address);
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
@ -435,34 +438,49 @@ ReturnValue_t StrComHandler::performFlashWrite() {
if (result != returnvalue::OK) { if (result != returnvalue::OK) {
return result; return result;
} }
bytesWritten += CHUNK_SIZE; totalBytesWritten += CHUNK_SIZE;
bytesWrittenInRegion += CHUNK_SIZE;
#if OBSW_DEBUG_STARTRACKER == 1 #if OBSW_DEBUG_STARTRACKER == 1
progressPrinter.print(idx * CHUNK_SIZE); progressPrinter.print(chunkIdx * CHUNK_SIZE);
#endif /* OBSW_DEBUG_STARTRACKER == 1 */ #endif /* OBSW_DEBUG_STARTRACKER == 1 */
return result;
};
for (uint32_t idx = 0; idx < fileChunks; idx++) {
if (terminate) {
return returnvalue::OK;
}
result = writeNextSegment(idx);
if (result != returnvalue::OK) {
return result;
}
if (idx % 50 == 0) { if (idx % 50 == 0) {
// Some grace time for other tasks // Some grace time for other tasks
TaskFactory::delayTask(2); TaskFactory::delayTask(2);
} }
} }
uint32_t remainingBytes = fileSize - fileChunks * CHUNK_SIZE; uint32_t remainingBytes = fileSize - fileChunks * CHUNK_SIZE;
file.seekg((fileChunks - 1) * CHUNK_SIZE, file.beg); if (remainingBytes > 0) {
file.read(reinterpret_cast<char*>(req.data), remainingBytes); file.seekg(fileChunks * CHUNK_SIZE, file.beg);
file.close(); file.read(reinterpret_cast<char*>(req.data), remainingBytes);
if (bytesWritten + CHUNK_SIZE > FLASH_REGION_SIZE) { file.close();
req.region++; if (bytesWrittenInRegion + CHUNK_SIZE > FLASH_REGION_SIZE) {
bytesWritten = 0; req.region++;
} bytesWrittenInRegion = 0;
req.address = bytesWritten; }
req.length = remainingBytes; req.address = bytesWrittenInRegion;
bytesWritten += remainingBytes; req.length = remainingBytes;
arc_pack_write_action_req(&req, cmdBuf.data(), &size); totalBytesWritten += CHUNK_SIZE;
result = sendAndRead(size, req.address); bytesWrittenInRegion += remainingBytes;
if (result != returnvalue::OK) { arc_pack_write_action_req(&req, cmdBuf.data(), &size);
return result; result = sendAndRead(size, req.address);
} if (result != returnvalue::OK) {
result = checkActionReply(replyLen); return result;
if (result != returnvalue::OK) { }
return result; result = checkActionReply(replyLen);
if (result != returnvalue::OK) {
return result;
}
} }
#if OBSW_DEBUG_STARTRACKER == 1 #if OBSW_DEBUG_STARTRACKER == 1
progressPrinter.print(fileSize); progressPrinter.print(fileSize);

View File

@ -11,8 +11,6 @@
#include "bsp_q7s/fs/SdCardManager.h" #include "bsp_q7s/fs/SdCardManager.h"
#endif #endif
#include "arcsec/client/generated/actionreq.h"
#include "arcsec/common/generated/tmtcstructs.h"
#include "fsfw/devicehandlers/CookieIF.h" #include "fsfw/devicehandlers/CookieIF.h"
#include "fsfw/objectmanager/SystemObject.h" #include "fsfw/objectmanager/SystemObject.h"
#include "fsfw/osal/linux/BinarySemaphore.h" #include "fsfw/osal/linux/BinarySemaphore.h"
@ -20,6 +18,10 @@
#include "fsfw/tasks/ExecutableObjectIF.h" #include "fsfw/tasks/ExecutableObjectIF.h"
#include "fsfw_hal/linux/serial/SerialComIF.h" #include "fsfw_hal/linux/serial/SerialComIF.h"
extern "C" {
#include <sagitta/client/arc_client.h>
}
/** /**
* @brief Helper class for the star tracker handler to accelerate large data transfers. * @brief Helper class for the star tracker handler to accelerate large data transfers.
* *

View File

@ -96,7 +96,7 @@ ReturnValue_t SusPolling::readReceivedMessage(CookieIF* cookie, uint8_t** buffer
if (susIdx < 0) { if (susIdx < 0) {
return FAILED; return FAILED;
} }
if(susDevs[susIdx].replyResult != returnvalue::OK) { if (susDevs[susIdx].replyResult != returnvalue::OK) {
return susDevs[susIdx].replyResult; return susDevs[susIdx].replyResult;
} }
MutexGuard mg(ipcLock); MutexGuard mg(ipcLock);
@ -170,7 +170,7 @@ ReturnValue_t SusPolling::handleSusPolling() {
susDevs[idx].ownReply.tempRaw = ((rawReply[0] & 0x0f) << 8) | rawReply[1]; susDevs[idx].ownReply.tempRaw = ((rawReply[0] & 0x0f) << 8) | rawReply[1];
// Reply is all ones. Sensor is probably off or faulty when // Reply is all ones. Sensor is probably off or faulty when
// it should not be. // it should not be.
if(susDevs[idx].ownReply.tempRaw == 0x0fff) { if (susDevs[idx].ownReply.tempRaw == 0x0fff) {
susDevs[idx].replyResult = returnvalue::FAILED; susDevs[idx].replyResult = returnvalue::FAILED;
} else { } else {
susDevs[idx].replyResult = returnvalue::OK; susDevs[idx].replyResult = returnvalue::OK;

View File

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

View File

@ -1,4 +1,8 @@
#include <mission/acs/str/ArcsecDatalinkLayer.h> #include "ArcsecDatalinkLayer.h"
extern "C" {
#include <wire/common/misc.h>
}
ArcsecDatalinkLayer::ArcsecDatalinkLayer() : decodeRingBuf(BUFFER_LENGTHS, true) { slipInit(); } ArcsecDatalinkLayer::ArcsecDatalinkLayer() : decodeRingBuf(BUFFER_LENGTHS, true) { slipInit(); }
@ -18,13 +22,10 @@ ReturnValue_t ArcsecDatalinkLayer::checkRingBufForFrame(const uint8_t** decodedF
case ARC_DEC_INPROGRESS: { case ARC_DEC_INPROGRESS: {
break; break;
} }
case ARC_DEC_ERROR_FRAME_SHORT: { case ARC_DEC_ERROR: {
decodeRingBuf.deleteData(idx); decodeRingBuf.deleteData(idx);
return REPLY_TOO_SHORT; return returnvalue::FAILED;
} }
case ARC_DEC_ERROR_CHECKSUM:
decodeRingBuf.deleteData(idx);
return CRC_FAILURE;
case ARC_DEC_ASYNC: case ARC_DEC_ASYNC:
case ARC_DEC_SYNC: { case ARC_DEC_SYNC: {
// Reset length of SLIP struct for next frame // Reset length of SLIP struct for next frame

View File

@ -5,10 +5,13 @@
#include <fsfw/devicehandlers/CookieIF.h> #include <fsfw/devicehandlers/CookieIF.h>
#include <mission/acs/str/strHelpers.h> #include <mission/acs/str/strHelpers.h>
#include "arcsec/common/misc.h"
#include "eive/resultClassIds.h" #include "eive/resultClassIds.h"
#include "fsfw/returnvalues/returnvalue.h" #include "fsfw/returnvalues/returnvalue.h"
extern "C" {
#include <wire/common/SLIP.h>
}
/** /**
* @brief Helper class to handle the datalinklayer of replies from the star tracker of arcsec. * @brief Helper class to handle the datalinklayer of replies from the star tracker of arcsec.
*/ */

View File

@ -3,6 +3,10 @@
#include "arcsecJsonKeys.h" #include "arcsecJsonKeys.h"
extern "C" {
#include <wire/common/genericstructs.h>
}
ArcsecJsonParamBase::ArcsecJsonParamBase(std::string setName) : setName(setName) {} ArcsecJsonParamBase::ArcsecJsonParamBase(std::string setName) : setName(setName) {}
ReturnValue_t ArcsecJsonParamBase::create(uint8_t* buffer) { ReturnValue_t ArcsecJsonParamBase::create(uint8_t* buffer) {

View File

@ -7,8 +7,6 @@
#include <fstream> #include <fstream>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include "arcsec/common/generated/tmtcstructs.h"
#include "arcsec/common/genericstructs.h"
#include "eive/resultClassIds.h" #include "eive/resultClassIds.h"
#include "fsfw/returnvalues/returnvalue.h" #include "fsfw/returnvalues/returnvalue.h"

View File

@ -1,18 +1,18 @@
#include <arcsec/client/generated/actionreq.h>
#include <arcsec/client/generated/parameter.h>
#include <arcsec/client/generated/telemetry.h>
#include <fsfw/ipc/QueueFactory.h> #include <fsfw/ipc/QueueFactory.h>
#include <fsfw/timemanager/Stopwatch.h> #include <fsfw/timemanager/Stopwatch.h>
#include <mission/acs/str/StarTrackerHandler.h> #include <mission/acs/str/StarTrackerHandler.h>
#include <mission/acs/str/strHelpers.h> #include <mission/acs/str/strHelpers.h>
#include <mission/acs/str/strJsonCommands.h> #include <mission/acs/str/strJsonCommands.h>
extern "C" {
#include <sagitta/client/arc_client.h>
}
#include <atomic> #include <atomic>
#include <fstream> #include <fstream>
#include <thread> #include <thread>
#include "OBSWConfig.h" #include "OBSWConfig.h"
#include "arcsec/common/misc.h"
std::atomic_bool JCFG_DONE(false); std::atomic_bool JCFG_DONE(false);

View File

@ -10,12 +10,15 @@
#include <thread> #include <thread>
#include "OBSWConfig.h" #include "OBSWConfig.h"
#include "arcsec/common/SLIP.h"
#include "devices/powerSwitcherList.h" #include "devices/powerSwitcherList.h"
#include "fsfw/devicehandlers/DeviceHandlerBase.h" #include "fsfw/devicehandlers/DeviceHandlerBase.h"
#include "fsfw/src/fsfw/serialize/SerializeAdapter.h" #include "fsfw/src/fsfw/serialize/SerializeAdapter.h"
#include "fsfw/timemanager/Countdown.h" #include "fsfw/timemanager/Countdown.h"
extern "C" {
#include <wire/common/SLIP.h>
}
/** /**
* @brief This is the device handler for the star tracker from arcsec. * @brief This is the device handler for the star tracker from arcsec.
* *

View File

@ -1 +1 @@
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_EBAND_UPPER_LIMITS 0
#define LOWER_PLOC_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), : ExtendedControllerBase(objectId),
heaterHandler(heater), heaterHandler(heater),
sensorTemperatures(this), sensorTemperatures(this),
@ -66,7 +67,8 @@ ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater
susSet8(objects::SUS_8_R_LOC_XBYBZB_PT_YB), susSet8(objects::SUS_8_R_LOC_XBYBZB_PT_YB),
susSet9(objects::SUS_9_R_LOC_XBYBZB_PT_YF), susSet9(objects::SUS_9_R_LOC_XBYBZB_PT_YF),
susSet10(objects::SUS_10_N_LOC_XMYBZF_PT_ZF), 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(); resetSensorsArray();
} }
@ -134,10 +136,12 @@ void ThermalController::performControlOperation() {
} }
} }
{ if (not tcsBrdShortlyUnavailable) {
PoolReadGuard pg(&sensorTemperatures); {
if (pg.getReadResult() == returnvalue::OK) { PoolReadGuard pg(&sensorTemperatures);
copySensors(); if (pg.getReadResult() == returnvalue::OK) {
copySensors();
}
} }
} }
{ {
@ -166,26 +170,31 @@ void ThermalController::performControlOperation() {
} }
} }
if (transitionToOff) { cycles++;
for (const auto& switchState : heaterSwitchStateArray) { if (transitionWhenHeatersOff) {
if (switchState != HeaterHandler::SwitchState::OFF) { bool allSwitchersOff = true;
transitionToOffCycles++; for (size_t idx = 0; idx < heaterSwitchStateArray.size(); idx++) {
// if heater still ON after 10 cycles, switch OFF again if (heaterSwitchStateArray[idx] != HeaterHandler::SwitchState::OFF) {
if (transitionToOffCycles == 10) { allSwitchersOff = false;
for (uint8_t i = 0; i < heater::Switchers::NUMBER_OF_SWITCHES; i++) { // if heater still ON after 3 cycles, switch OFF again
heaterHandler.switchHeater(static_cast<heater::Switchers>(i), if (transitionWhenHeatersOffCycles == 3) {
HeaterHandler::SwitchState::OFF); heaterHandler.switchHeater(static_cast<heater::Switch>(idx),
} HeaterHandler::SwitchState::OFF);
triggerEvent(tcsCtrl::HEATER_NOT_OFF_FOR_OFF_MODE); triggerEvent(tcsCtrl::HEATER_NOT_OFF_FOR_OFF_MODE);
} }
return;
} }
setMode(MODE_OFF);
} }
} else if (mode != MODE_OFF) { if (allSwitchersOff or transitionWhenHeatersOffCycles == 6) {
// Finish the transition
transitionWhenHeatersOff = false;
resetThermalStates();
setMode(targetMode, targetSubmode);
} else {
transitionWhenHeatersOffCycles++;
}
} else if (mode != MODE_OFF and not tcsBrdShortlyUnavailable) {
performThermalModuleCtrl(heaterSwitchStateArray); performThermalModuleCtrl(heaterSwitchStateArray);
} }
cycles++;
} }
ReturnValue_t ThermalController::initializeLocalDataPool(localpool::DataPool& localDataPoolMap, ReturnValue_t ThermalController::initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
@ -288,12 +297,18 @@ LocalPoolDataSetBase* ThermalController::getDataSetHandle(sid_t sid) {
ReturnValue_t ThermalController::checkModeCommand(Mode_t mode, Submode_t submode, ReturnValue_t ThermalController::checkModeCommand(Mode_t mode, Submode_t submode,
uint32_t* msToReachTheMode) { uint32_t* msToReachTheMode) {
if ((mode != MODE_OFF) and (mode != MODE_ON)) {
return INVALID_MODE;
}
if (mode == MODE_ON) {
if (submode != SUBMODE_NONE and submode != SUBMODE_NO_HEATER_CTRL) {
return HasModesIF::INVALID_SUBMODE;
}
return returnvalue::OK;
}
if (submode != SUBMODE_NONE) { if (submode != SUBMODE_NONE) {
return INVALID_SUBMODE; return INVALID_SUBMODE;
} }
if ((mode != MODE_OFF) && (mode != MODE_ON) && (mode != MODE_NORMAL)) {
return INVALID_MODE;
}
return returnvalue::OK; return returnvalue::OK;
} }
@ -972,8 +987,8 @@ void ThermalController::copyDevices() {
} }
void ThermalController::ctrlAcsBoard() { void ThermalController::ctrlAcsBoard() {
heater::Switchers switchNr = heater::HEATER_2_ACS_BRD; heater::Switch switchNr = heater::HEATER_2_ACS_BRD;
heater::Switchers redSwitchNr = heater::HEATER_0_OBC_BRD; heater::Switch redSwitchNr = heater::HEATER_0_OBC_BRD;
// A side // A side
thermalComponent = ACS_BOARD; thermalComponent = ACS_BOARD;
@ -1019,7 +1034,9 @@ void ThermalController::ctrlAcsBoard() {
} else { } else {
if (chooseHeater(switchNr, redSwitchNr)) { if (chooseHeater(switchNr, redSwitchNr)) {
if (heaterHandler.getSwitchState(switchNr)) { if (heaterHandler.getSwitchState(switchNr)) {
heaterHandler.switchHeater(switchNr, HeaterHandler::SwitchState::OFF); if (submode != SUBMODE_NO_HEATER_CTRL) {
heaterSwitchHelper(switchNr, HeaterHandler::SwitchState::OFF, thermalComponent);
}
} }
} }
} }
@ -1264,8 +1281,8 @@ void ThermalController::ctrlPcduP60Board() {
void ThermalController::ctrlPcduAcu() { void ThermalController::ctrlPcduAcu() {
thermalComponent = PCDUACU; thermalComponent = PCDUACU;
heater::Switchers switchNr = heater::HEATER_3_PCDU_PDU; heater::Switch switchNr = heater::HEATER_3_PCDU_PDU;
heater::Switchers redSwitchNr = heater::HEATER_2_ACS_BRD; heater::Switch redSwitchNr = heater::HEATER_2_ACS_BRD;
if (chooseHeater(switchNr, redSwitchNr)) { if (chooseHeater(switchNr, redSwitchNr)) {
bool sensorTempAvailable = true; bool sensorTempAvailable = true;
@ -1553,13 +1570,16 @@ void ThermalController::performThermalModuleCtrl(const HeaterSwitchStates& heate
void ThermalController::ctrlComponentTemperature(HeaterContext& htrCtx) { void ThermalController::ctrlComponentTemperature(HeaterContext& htrCtx) {
if (selectAndReadSensorTemp(htrCtx)) { if (selectAndReadSensorTemp(htrCtx)) {
if (chooseHeater(htrCtx.switchNr, htrCtx.redSwitchNr)) { if (chooseHeater(htrCtx.switchNr, htrCtx.redSwitchNr)) {
// Core loop for a thermal component, after sensors and heaters were selected.
checkLimitsAndCtrlHeater(htrCtx); checkLimitsAndCtrlHeater(htrCtx);
} }
} else { } else {
// TODO: muss der Heater dann wirklich abgeschalten werden? // No sensors available, so switch the heater off. We can not perform control tasks if we
// are blind..
if (chooseHeater(htrCtx.switchNr, htrCtx.redSwitchNr)) { if (chooseHeater(htrCtx.switchNr, htrCtx.redSwitchNr)) {
if (heaterHandler.getSwitchState(htrCtx.switchNr)) { if (heaterCtrlAllowed() and
heaterHandler.switchHeater(htrCtx.switchNr, HeaterHandler::SwitchState::OFF); (heaterHandler.getSwitchState(htrCtx.switchNr) == HeaterHandler::SwitchState::ON)) {
heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::OFF, thermalComponent);
} }
} }
} }
@ -1589,7 +1609,7 @@ bool ThermalController::selectAndReadSensorTemp(HeaterContext& htrCtx) {
return false; return false;
} }
bool ThermalController::chooseHeater(heater::Switchers& switchNr, heater::Switchers redSwitchNr) { bool ThermalController::chooseHeater(heater::Switch& switchNr, heater::Switch redSwitchNr) {
bool heaterAvailable = true; bool heaterAvailable = true;
if (heaterHandler.getHealth(switchNr) != HasHealthIF::HEALTHY) { if (heaterHandler.getHealth(switchNr) != HasHealthIF::HEALTHY) {
@ -1607,15 +1627,18 @@ bool ThermalController::chooseHeater(heater::Switchers& switchNr, heater::Switch
} }
void ThermalController::heaterCtrlTempTooHighHandler(HeaterContext& htrCtx, const char* whatLimit) { void ThermalController::heaterCtrlTempTooHighHandler(HeaterContext& htrCtx, const char* whatLimit) {
if (not heaterCtrlAllowed()) {
return;
}
if (htrCtx.switchState == HeaterHandler::SwitchState::ON) { if (htrCtx.switchState == HeaterHandler::SwitchState::ON) {
sif::info << "TCS: Component " << static_cast<int>(thermalComponent) << " too warm, above " sif::info << "TCS: Component " << static_cast<int>(thermalComponent) << " too warm, above "
<< whatLimit << ", switching off heater" << std::endl; << whatLimit << ", switching off heater" << std::endl;
heaterHandler.switchHeater(htrCtx.switchNr, HeaterHandler::SwitchState::OFF); heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::OFF, thermalComponent);
heaterStates[htrCtx.switchNr].switchTransition = true; heaterStates[htrCtx.switchNr].switchTransition = true;
heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::OFF; heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::OFF;
} }
if (heaterHandler.getSwitchState(htrCtx.redSwitchNr) == HeaterHandler::SwitchState::ON) { if (heaterHandler.getSwitchState(htrCtx.redSwitchNr) == HeaterHandler::SwitchState::ON) {
heaterHandler.switchHeater(htrCtx.redSwitchNr, HeaterHandler::SwitchState::OFF); heaterSwitchHelper(htrCtx.redSwitchNr, HeaterHandler::SwitchState::OFF, thermalComponent);
heaterStates[htrCtx.redSwitchNr].switchTransition = true; heaterStates[htrCtx.redSwitchNr].switchTransition = true;
heaterStates[htrCtx.redSwitchNr].target = HeaterHandler::SwitchState::OFF; heaterStates[htrCtx.redSwitchNr].target = HeaterHandler::SwitchState::OFF;
} }
@ -1630,43 +1653,44 @@ void ThermalController::checkLimitsAndCtrlHeater(HeaterContext& htrCtx) {
if (heaterStates[htrCtx.switchNr].switchTransition) { if (heaterStates[htrCtx.switchNr].switchTransition) {
htrCtx.doHeaterHandling = false; htrCtx.doHeaterHandling = false;
heaterCtrlCheckUpperLimits(htrCtx); heaterCtrlCheckUpperLimits(htrCtx);
} else { return;
// Heater off }
htrCtx.switchState = heaterHandler.getSwitchState(htrCtx.switchNr);
if (htrCtx.switchState == HeaterHandler::SwitchState::OFF) { htrCtx.switchState = heaterHandler.getSwitchState(htrCtx.switchNr);
if (sensorTemp < htrCtx.tempLimit.opLowerLimit) { // Heater off
heaterHandler.switchHeater(htrCtx.switchNr, HeaterHandler::SwitchState::ON); if (htrCtx.switchState == HeaterHandler::SwitchState::OFF) {
sif::info << "ThermalController::checkLimitsAndCtrlHeater: Heater " if (sensorTemp < htrCtx.tempLimit.opLowerLimit and heaterCtrlAllowed()) {
<< static_cast<int>(thermalComponent) << " ON" << std::endl; sif::info << "TCS: Heater " << static_cast<int>(thermalComponent) << " ON" << std::endl;
heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::ON, thermalComponent);
heaterStates[htrCtx.switchNr].switchTransition = true;
heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::ON;
} else {
// Even if heater control is now allowed, we can update the state.
thermalStates[thermalComponent].heating = false;
}
heaterCtrlCheckUpperLimits(htrCtx);
return;
}
// Heater on
if (htrCtx.switchState == HeaterHandler::SwitchState::ON) {
if (thermalStates[thermalComponent].heating) {
// We are already in a heating cycle, so need to check whether heating task is complete.
if (sensorTemp >= htrCtx.tempLimit.opLowerLimit + TEMP_OFFSET and heaterCtrlAllowed()) {
sif::info << "TCS: Heater " << static_cast<int>(thermalComponent) << " OFF" << std::endl;
heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::OFF, thermalComponent);
heaterStates[htrCtx.switchNr].switchTransition = true; heaterStates[htrCtx.switchNr].switchTransition = true;
thermalStates[thermalComponent].heating = true; heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::OFF;
heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::ON;
} else {
thermalStates[thermalComponent].heating = false;
} }
heaterCtrlCheckUpperLimits(htrCtx); return;
// Heater on }
} else if (heaterHandler.getSwitchState(htrCtx.switchNr) == HeaterHandler::SwitchState::ON) { // This can happen if heater is used as alternative heater (no regular heating cycle), so we
if (thermalStates[thermalComponent].heating) { // should still check the upper limits.
// We are already in a heating cycle, so need to check whether heating task is complete. bool tooHighHandlerAlreadyCalled = heaterCtrlCheckUpperLimits(htrCtx);
if (sensorTemp >= htrCtx.tempLimit.opLowerLimit + TEMP_OFFSET) { if (sensorTemp >= htrCtx.tempLimit.cutOffLimit) {
heaterHandler.switchHeater(htrCtx.switchNr, HeaterHandler::SwitchState::OFF); componentAboveCutOffLimit = true;
sif::info << "ThermalController::checkLimitsAndCtrlHeater: Heater " if (not tooHighHandlerAlreadyCalled) {
<< static_cast<int>(thermalComponent) << " OFF" << std::endl; heaterCtrlTempTooHighHandler(htrCtx, "CutOff-Limit");
heaterStates[htrCtx.switchNr].switchTransition = true;
heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::OFF;
thermalStates[thermalComponent].heating = false;
}
} else {
// This can happen if heater is used as alternative heater (no regular heating cycle), so we
// should still check the upper limits.
bool tooHighHandlerAlreadyCalled = heaterCtrlCheckUpperLimits(htrCtx);
if (sensorTemp >= htrCtx.tempLimit.cutOffLimit) {
componentAboveCutOffLimit = true;
if (not tooHighHandlerAlreadyCalled) {
heaterCtrlTempTooHighHandler(htrCtx, "CutOff-Limit");
}
}
} }
} }
} }
@ -1698,8 +1722,8 @@ void ThermalController::resetSensorsArray() {
} }
thermalComponent = NONE; thermalComponent = NONE;
} }
void ThermalController::heaterTransitionControl(const HeaterSwitchStates& currentHeaterStates) { void ThermalController::heaterTransitionControl(const HeaterSwitchStates& currentHeaterStates) {
// TODO: Test
for (unsigned i = 0; i < 7; i++) { for (unsigned i = 0; i < 7; i++) {
if (heaterStates[i].switchTransition) { if (heaterStates[i].switchTransition) {
if (currentHeaterStates[i] == heaterStates[i].target) { if (currentHeaterStates[i] == heaterStates[i].target) {
@ -1723,11 +1747,12 @@ uint32_t ThermalController::tempFloatToU32() const {
return tempRaw; return tempRaw;
} }
void ThermalController::setMode(Mode_t mode) { void ThermalController::setMode(Mode_t mode, Submode_t submode) {
if (mode == MODE_OFF) { if (mode == MODE_OFF) {
transitionToOff = false; transitionWhenHeatersOff = false;
} }
this->mode = mode; this->mode = mode;
this->submode = submode;
modeHelper.modeChanged(mode, submode); modeHelper.modeChanged(mode, submode);
announceMode(false); announceMode(false);
} }
@ -1742,6 +1767,43 @@ bool ThermalController::tooHotHandler(object_id_t object, bool& oneShotFlag) {
return false; return false;
} }
bool ThermalController::heaterCtrlAllowed() const { return submode != SUBMODE_NO_HEATER_CTRL; }
void ThermalController::resetThermalStates() {
for (auto& thermalState : thermalStates) {
thermalState.heating = false;
}
}
void ThermalController::heaterSwitchHelper(heater::Switch switchNr,
HeaterHandler::SwitchState state,
unsigned componentIdx) {
timeval currentTime;
Clock::getClockMonotonic(&currentTime);
if (state == HeaterHandler::SwitchState::ON) {
heaterHandler.switchHeater(switchNr, state);
thermalStates[componentIdx].heating = true;
thermalStates[componentIdx].heaterStartTime = currentTime.tv_sec;
} else {
heaterHandler.switchHeater(switchNr, state);
thermalStates[componentIdx].heating = false;
thermalStates[componentIdx].heaterEndTime = currentTime.tv_sec;
}
}
void ThermalController::heaterSwitchHelperAllOff() {
timeval currentTime;
Clock::getClockMonotonic(&currentTime);
size_t idx = 0;
for (; idx < heater::Switch::NUMBER_OF_SWITCHES; idx++) {
heaterHandler.switchHeater(static_cast<heater::Switch>(idx), HeaterHandler::SwitchState::OFF);
}
for (idx = 0; idx < thermalStates.size(); idx++) {
thermalStates[idx].heating = false;
thermalStates[idx].heaterEndTime = currentTime.tv_sec;
}
}
void ThermalController::tooHotHandlerWhichClearsOneShotFlag(object_id_t object, bool& oneShotFlag) { void ThermalController::tooHotHandlerWhichClearsOneShotFlag(object_id_t object, bool& oneShotFlag) {
// Clear the one shot flag is the component is in acceptable temperature range. // Clear the one shot flag is the component is in acceptable temperature range.
if (not tooHotHandler(object, oneShotFlag) and not componentAboveUpperLimit) { if (not tooHotHandler(object, oneShotFlag) and not componentAboveUpperLimit) {
@ -1751,14 +1813,15 @@ void ThermalController::tooHotHandlerWhichClearsOneShotFlag(object_id_t object,
void ThermalController::startTransition(Mode_t mode_, Submode_t submode_) { void ThermalController::startTransition(Mode_t mode_, Submode_t submode_) {
triggerEvent(CHANGING_MODE, mode_, submode_); triggerEvent(CHANGING_MODE, mode_, submode_);
if (mode_ == MODE_OFF) { // For MODE_OFF and the no heater control submode, we command all switches to off before
for (uint8_t i = 0; i < heater::Switchers::NUMBER_OF_SWITCHES; i++) { // completing the transition. This ensures a consistent state when commanding these modes.
heaterHandler.switchHeater(static_cast<heater::Switchers>(i), if ((mode_ == MODE_OFF) or ((mode_ == MODE_ON) and (submode_ == SUBMODE_NO_HEATER_CTRL))) {
HeaterHandler::SwitchState::OFF); heaterSwitchHelperAllOff();
} transitionWhenHeatersOff = true;
transitionToOff = true; targetMode = mode_;
transitionToOffCycles = 0; targetSubmode = submode_;
transitionWhenHeatersOffCycles = 0;
} else { } else {
setMode(mode_); setMode(mode_, submode_);
} }
} }

View File

@ -22,6 +22,7 @@
#include <mission/tcs/Tmp1075Definitions.h> #include <mission/tcs/Tmp1075Definitions.h>
#include <mission/utility/trace.h> #include <mission/utility/trace.h>
#include <atomic>
#include <list> #include <list>
/** /**
@ -47,8 +48,13 @@ struct TempLimits {
struct ThermalState { struct ThermalState {
uint8_t errorCounter; uint8_t errorCounter;
bool heating; // Is heating on for that thermal module?
uint32_t heaterStartTime; bool heating = false;
heater::Switch heaterSwitch = heater::Switch::NUMBER_OF_SWITCHES;
// Heater start time and end times as UNIX seconds. Please note that these times will be updated
// when a switch command is sent, with no guarantess that the heater actually went on.
uint32_t heaterStartTime = 0;
uint32_t heaterEndTime = 0;
}; };
struct HeaterState { struct HeaterState {
@ -89,25 +95,28 @@ enum ThermalComponents : uint8_t {
class ThermalController : public ExtendedControllerBase { class ThermalController : public ExtendedControllerBase {
public: public:
static constexpr uint8_t SUBMODE_NO_HEATER_CTRL = 1;
static const uint16_t INVALID_TEMPERATURE = 999; static const uint16_t INVALID_TEMPERATURE = 999;
static const uint8_t NUMBER_OF_SENSORS = 16; static const uint8_t NUMBER_OF_SENSORS = 16;
static constexpr int16_t SANITY_LIMIT_LOWER_TEMP = -80; static constexpr int16_t SANITY_LIMIT_LOWER_TEMP = -80;
static constexpr int16_t SANITY_LIMIT_UPPER_TEMP = 160; 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; ReturnValue_t initialize() override;
protected: protected:
struct HeaterContext { struct HeaterContext {
public: public:
HeaterContext(heater::Switchers switchNr, heater::Switchers redundantSwitchNr, HeaterContext(heater::Switch switchNr, heater::Switch redundantSwitchNr,
const TempLimits& tempLimit) const TempLimits& tempLimit)
: switchNr(switchNr), redSwitchNr(redundantSwitchNr), tempLimit(tempLimit) {} : switchNr(switchNr), redSwitchNr(redundantSwitchNr), tempLimit(tempLimit) {}
bool doHeaterHandling = true; bool doHeaterHandling = true;
heater::Switchers switchNr; heater::Switch switchNr;
HeaterHandler::SwitchState switchState = HeaterHandler::SwitchState::OFF; HeaterHandler::SwitchState switchState = HeaterHandler::SwitchState::OFF;
heater::Switchers redSwitchNr; heater::Switch redSwitchNr;
const TempLimits& tempLimit; const TempLimits& tempLimit;
}; };
@ -181,6 +190,10 @@ class ThermalController : public ExtendedControllerBase {
susMax1227::SusDataset susSet10; susMax1227::SusDataset susSet10;
susMax1227::SusDataset susSet11; 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<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> 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); lp_var_t<int16_t> battTemp2 = lp_var_t<int16_t>(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_2);
@ -257,11 +270,13 @@ class ThermalController : public ExtendedControllerBase {
bool strTooHotFlag = false; bool strTooHotFlag = false;
bool rwTooHotFlag = false; bool rwTooHotFlag = false;
bool transitionToOff = false; bool transitionWhenHeatersOff = false;
uint32_t transitionToOffCycles = 0; uint32_t transitionWhenHeatersOffCycles = 0;
Mode_t targetMode = MODE_OFF;
Submode_t targetSubmode = SUBMODE_NONE;
uint32_t cycles = 0; uint32_t cycles = 0;
std::array<ThermalState, 30> thermalStates{}; std::array<ThermalState, ThermalComponents::NUM_ENTRIES> thermalStates{};
std::array<HeaterState, 7> heaterStates{}; std::array<HeaterState, heater::NUMBER_OF_SWITCHES> heaterStates{};
// Initial delay to make sure all pool variables have been initialized their owners. // Initial delay to make sure all pool variables have been initialized their owners.
// Also, wait for system initialization to complete. // Also, wait for system initialization to complete.
@ -286,6 +301,9 @@ class ThermalController : public ExtendedControllerBase {
void startTransition(Mode_t mode, Submode_t submode) override; void startTransition(Mode_t mode, Submode_t submode) override;
bool heaterCtrlAllowed() const;
void resetThermalStates();
void resetSensorsArray(); void resetSensorsArray();
void copySensors(); void copySensors();
void copySus(); void copySus();
@ -296,9 +314,13 @@ class ThermalController : public ExtendedControllerBase {
bool heaterCtrlCheckUpperLimits(HeaterContext& heaterContext); bool heaterCtrlCheckUpperLimits(HeaterContext& heaterContext);
void heaterCtrlTempTooHighHandler(HeaterContext& heaterContext, const char* whatLimit); void heaterCtrlTempTooHighHandler(HeaterContext& heaterContext, const char* whatLimit);
bool chooseHeater(heater::Switchers& switchNr, heater::Switchers redSwitchNr); bool chooseHeater(heater::Switch& switchNr, heater::Switch redSwitchNr);
bool selectAndReadSensorTemp(HeaterContext& htrCtx); bool selectAndReadSensorTemp(HeaterContext& htrCtx);
void heaterSwitchHelperAllOff();
void heaterSwitchHelper(heater::Switch switchNr, HeaterHandler::SwitchState state,
unsigned componentIdx);
void ctrlAcsBoard(); void ctrlAcsBoard();
void ctrlMgt(); void ctrlMgt();
void ctrlRw(); void ctrlRw();
@ -323,7 +345,7 @@ class ThermalController : public ExtendedControllerBase {
void ctrlMpa(); void ctrlMpa();
void ctrlScexBoard(); void ctrlScexBoard();
void heaterTransitionControl(const HeaterSwitchStates& currentHeaterStates); void heaterTransitionControl(const HeaterSwitchStates& currentHeaterStates);
void setMode(Mode_t mode); void setMode(Mode_t mode, Submode_t submode);
uint32_t tempFloatToU32() const; uint32_t tempFloatToU32() const;
bool tooHotHandler(object_id_t object, bool& oneShotFlag); bool tooHotHandler(object_id_t object, bool& oneShotFlag);
void tooHotHandlerWhichClearsOneShotFlag(object_id_t object, bool& oneShotFlag); void tooHotHandlerWhichClearsOneShotFlag(object_id_t object, bool& oneShotFlag);

View File

@ -4,8 +4,8 @@
#include <fsfw/datapoollocal/LocalPoolVariable.h> #include <fsfw/datapoollocal/LocalPoolVariable.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h> #include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include "devices/heaterSwitcherList.h"
#include "eive/eventSubsystemIds.h" #include "eive/eventSubsystemIds.h"
#include "mission/tcs/defs.h"
namespace tcsCtrl { namespace tcsCtrl {

View File

@ -1,5 +1,4 @@
#include <fsfw/cfdp/CfdpDistributor.h> #include <fsfw/cfdp/CfdpDistributor.h>
#include <fsfw/cfdp/handler/CfdpHandler.h>
#include <fsfw/cfdp/handler/RemoteConfigTableIF.h> #include <fsfw/cfdp/handler/RemoteConfigTableIF.h>
#include <fsfw/controller/ControllerBase.h> #include <fsfw/controller/ControllerBase.h>
#include <fsfw/controller/ExtendedControllerBase.h> #include <fsfw/controller/ExtendedControllerBase.h>
@ -23,6 +22,7 @@
#include <fsfw/tcdistribution/PusDistributor.h> #include <fsfw/tcdistribution/PusDistributor.h>
#include <fsfw/timemanager/CdsShortTimeStamper.h> #include <fsfw/timemanager/CdsShortTimeStamper.h>
#include <fsfw_hal/host/HostFilesystem.h> #include <fsfw_hal/host/HostFilesystem.h>
#include <mission/cfdp/CfdpHandler.h>
#include <mission/controller/ThermalController.h> #include <mission/controller/ThermalController.h>
#include <mission/genericFactory.h> #include <mission/genericFactory.h>
#include <mission/persistentTmStoreDefs.h> #include <mission/persistentTmStoreDefs.h>
@ -48,6 +48,7 @@
#include "mission/system/acs/RwAssembly.h" #include "mission/system/acs/RwAssembly.h"
#include "mission/system/acs/acsModeTree.h" #include "mission/system/acs/acsModeTree.h"
#include "mission/system/tree/tcsModeTree.h" #include "mission/system/tree/tcsModeTree.h"
#include "mission/tcs/defs.h"
#include "mission/tmtc/tmFilters.h" #include "mission/tmtc/tmFilters.h"
#include "objects/systemObjectList.h" #include "objects/systemObjectList.h"
#include "tmtc/pusIds.h" #include "tmtc/pusIds.h"
@ -90,6 +91,8 @@ EiveFaultHandler EIVE_FAULT_HANDLER;
} // namespace cfdp } // namespace cfdp
std::atomic_bool tcs::TCS_BOARD_SHORTLY_UNAVAILABLE = false;
void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFunnel** pusFunnel, void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFunnel** pusFunnel,
CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan, CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan,
StorageManagerIF** ipcStore, StorageManagerIF** tmStore, StorageManagerIF** ipcStore, StorageManagerIF** tmStore,
@ -300,7 +303,8 @@ void ObjectFactory::createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF&
} }
void ObjectFactory::createThermalController(HeaterHandler& heaterHandler) { 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); tcsCtrl->connectModeTreeParent(satsystem::tcs::SUBSYSTEM);
} }
void ObjectFactory::createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch, void ObjectFactory::createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch,
@ -366,10 +370,12 @@ void ObjectFactory::createAcsBoardAssy(PowerSwitchIF& pwrSwitcher,
acsAss->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM); acsAss->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM);
} }
TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher) { TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher,
std::atomic_bool& tcsShortlyUnavailable) {
TcsBoardHelper helper(RTD_INFOS); TcsBoardHelper helper(RTD_INFOS);
auto* tcsBoardAss = new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher, auto* tcsBoardAss =
power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper); new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher,
power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper, tcsShortlyUnavailable);
tcsBoardAss->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); tcsBoardAss->connectModeTreeParent(satsystem::tcs::SUBSYSTEM);
return tcsBoardAss; 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 createSusAssy(PowerSwitchIF& pwrSwitcher, std::array<DeviceHandlerBase*, 12> suses);
void createAcsBoardAssy(PowerSwitchIF& pwrSwitcher, std::array<DeviceHandlerBase*, 8> assemblyDhbs, void createAcsBoardAssy(PowerSwitchIF& pwrSwitcher, std::array<DeviceHandlerBase*, 8> assemblyDhbs,
ExtendedControllerBase* gpsCtrl, GpioIF* gpioComIF); ExtendedControllerBase* gpsCtrl, GpioIF* gpioComIF);
TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher); TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher,
std::atomic_bool& tcsShortlyUnavailable);
} // namespace ObjectFactory } // 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 States { IDLE, SWITCHING_POWER, CHECKING_POWER, MODE_COMMANDING };
enum class OpCodes { NONE, TO_OFF_DONE, TO_NOT_OFF_DONE, TIMEOUT_OCCURED }; 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 power
namespace duallane { namespace duallane {

View File

@ -13,7 +13,25 @@ enum Mode : Mode_t { BOOT = 5, SAFE = acs::AcsMode::SAFE, PTG_IDLE = acs::AcsMod
} }
namespace xsc {
enum Chip : int { CHIP_0, CHIP_1, NO_CHIP, SELF_CHIP, ALL_CHIP };
enum Copy : int { COPY_0, COPY_1, NO_COPY, SELF_COPY, ALL_COPY };
} // namespace xsc
namespace core { namespace core {
// TODO: Support for status? Or maybe some command to quickly get information whether a unit
// is running.
enum SystemctlCmd : uint8_t { START = 0, STOP = 1, RESTART = 2, NUM_CMDS = 3 };
static constexpr char CONF_FOLDER[] = "conf";
static constexpr char VERSION_FILE_NAME[] = "version.txt";
static constexpr char REBOOT_FILE_NAME[] = "reboot.txt";
static constexpr char TIME_FILE_NAME[] = "time_backup.txt";
static constexpr ActionId_t LIST_DIRECTORY_INTO_FILE = 0; static constexpr ActionId_t LIST_DIRECTORY_INTO_FILE = 0;
static constexpr ActionId_t ANNOUNCE_VERSION = 1; static constexpr ActionId_t ANNOUNCE_VERSION = 1;
static constexpr ActionId_t ANNOUNCE_CURRENT_IMAGE = 2; static constexpr ActionId_t ANNOUNCE_CURRENT_IMAGE = 2;
@ -37,7 +55,9 @@ static constexpr ActionId_t MOUNT_OTHER_COPY = 33;
//! Reboot using the reboot command //! Reboot using the reboot command
static constexpr ActionId_t REBOOT_OBC = 34; static constexpr ActionId_t REBOOT_OBC = 34;
static constexpr ActionId_t EXECUTE_SHELL_CMD = 40; static constexpr ActionId_t EXECUTE_SHELL_CMD_BLOCKING = 40;
static constexpr ActionId_t EXECUTE_SHELL_CMD_NON_BLOCKING = 41;
static constexpr ActionId_t SYSTEMCTL_CMD_EXECUTOR = 42;
static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::CORE; static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::CORE;

View File

@ -6,5 +6,6 @@ add_subdirectory(fdir)
add_subdirectory(power) add_subdirectory(power)
target_sources( target_sources(
${LIB_EIVE_MISSION} PRIVATE systemTree.cpp DualLanePowerStateMachine.cpp ${LIB_EIVE_MISSION}
EiveSystem.cpp treeUtil.cpp) 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; SideSwitchState sideSwitchState = SideSwitchState::NONE;
duallane::Submodes targetSubmodeForSideSwitch = duallane::Submodes::B_SIDE; duallane::Submodes targetSubmodeForSideSwitch = duallane::Submodes::B_SIDE;
enum RecoveryCustomStates { power::RecoveryCustomStates customRecoveryStates = power::RecoveryCustomStates::IDLE;
IDLE,
POWER_SWITCHING_OFF,
POWER_SWITCHING_ON,
DONE
} customRecoveryStates = RecoveryCustomStates::IDLE;
MessageQueueIF* eventQueue = nullptr; MessageQueueIF* eventQueue = nullptr;

View File

@ -1,8 +1,8 @@
#include "RwAssembly.h" #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) RwHelper helper)
: AssemblyBase(objectId), helper(helper), switcher(pwrSwitcher, switcher) { : SharedPowerAssemblyBase(objectId, pwrSwitcher, switchId), helper(helper) {
ModeListEntry entry; ModeListEntry entry;
for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) { for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) {
entry.setObject(helper.rwIds[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 RwAssembly::commandChildren(Mode_t mode, Submode_t submode) {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
modeTransitionFailedSwitch = true;
// Initialize the mode table to ensure all devices are in a defined state // Initialize the mode table to ensure all devices are in a defined state
for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) { for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) {
modeTable[idx].setMode(MODE_OFF); modeTable[idx].setMode(MODE_OFF);
@ -76,36 +58,6 @@ ReturnValue_t RwAssembly::isModeCombinationValid(Mode_t mode, Submode_t submode)
return HasModesIF::INVALID_MODE; 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 RwAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode) {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
object_id_t objId = 0; object_id_t objId = 0;

View File

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

View File

@ -3,4 +3,4 @@
#include "eive/objects.h" #include "eive/objects.h"
RtdFdir::RtdFdir(object_id_t sensorId) 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> #include <fsfw/ipc/QueueFactory.h>
TcsBoardAssembly::TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, TcsBoardAssembly::TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher,
power::Switch_t theSwitch, TcsBoardHelper helper) power::Switch_t theSwitch, TcsBoardHelper helper,
: AssemblyBase(objectId, 24), switcher(pwrSwitcher, theSwitch), helper(helper) { std::atomic_bool& tcsShortlyUnavailable)
eventQueue = QueueFactory::instance()->createMessageQueue(24); : SharedPowerAssemblyBase(objectId, pwrSwitcher, theSwitch, 16),
helper(helper),
tcsShortlyUnavailable(tcsShortlyUnavailable) {
ModeListEntry entry; ModeListEntry entry;
for (uint8_t idx = 0; idx < NUMBER_RTDS; idx++) { for (uint8_t idx = 0; idx < NUMBER_RTDS; idx++) {
entry.setObject(helper.rtdInfos[idx].first); 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 TcsBoardAssembly::commandChildren(Mode_t mode, Submode_t submode) {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
// Initialize the mode table to ensure all devices are in a defined state // 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) { if (mode == DeviceHandlerIF::MODE_NORMAL or mode == MODE_ON) {
result = handleNormalOrOnModeCmd(mode, submode); result = handleNormalOrOnModeCmd(mode, submode);
} }
} else {
tcsShortlyUnavailable = true;
} }
HybridIterator<ModeListEntry> tableIter(modeTable.begin(), modeTable.end()); HybridIterator<ModeListEntry> tableIter(modeTable.begin(), modeTable.end());
executeTable(tableIter); executeTable(tableIter);
@ -94,25 +81,6 @@ ReturnValue_t TcsBoardAssembly::isModeCombinationValid(Mode_t mode, Submode_t su
return HasModesIF::INVALID_MODE; 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 TcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode) {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
bool needsSecondStep = false; bool needsSecondStep = false;
@ -169,21 +137,6 @@ bool TcsBoardAssembly::isUseable(object_id_t object, Mode_t mode) {
return false; 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) { void TcsBoardAssembly::handleChildrenLostMode(ReturnValue_t result) {
triggerEvent(CHILDREN_LOST_MODE, result); triggerEvent(CHILDREN_LOST_MODE, result);
startTransition(mode, submode); startTransition(mode, submode);
@ -210,6 +163,12 @@ ReturnValue_t TcsBoardAssembly::checkAndHandleHealthStates(Mode_t deviceMode,
return status; return status;
} }
bool TcsBoardAssembly::checkAndHandleRecovery() {
bool recoveryPending = SharedPowerAssemblyBase::checkAndHandleRecovery();
tcsShortlyUnavailable = recoveryPending;
return recoveryPending;
}
void TcsBoardAssembly::handleModeTransitionFailed(ReturnValue_t result) { void TcsBoardAssembly::handleModeTransitionFailed(ReturnValue_t result) {
if (targetMode == MODE_OFF) { if (targetMode == MODE_OFF) {
AssemblyBase::handleModeTransitionFailed(result); AssemblyBase::handleModeTransitionFailed(result);

View File

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

View File

@ -111,7 +111,7 @@ void buildNormalSequence(Subsystem& ss, ModeListEntry& eh) {
ctxc); ctxc);
// Transition 1 // Transition 1
iht(objects::THERMAL_CONTROLLER, NML, 0, TCS_TABLE_NORMAL_TRANS_1.second); iht(objects::THERMAL_CONTROLLER, HasModesIF::MODE_ON, 0, TCS_TABLE_NORMAL_TRANS_1.second);
check(ss.addTable(TableEntry(TCS_TABLE_NORMAL_TRANS_1.first, &TCS_TABLE_NORMAL_TRANS_1.second)), check(ss.addTable(TableEntry(TCS_TABLE_NORMAL_TRANS_1.first, &TCS_TABLE_NORMAL_TRANS_1.second)),
ctxc); ctxc);

View File

@ -215,17 +215,17 @@ void HeaterHandler::handleSwitchHandling() {
heaterVec[idx].cmdActive = true; heaterVec[idx].cmdActive = true;
heaterVec[idx].action = SET_SWITCH_OFF; heaterVec[idx].action = SET_SWITCH_OFF;
triggerEvent(FAULTY_HEATER_WAS_ON, idx, 0); triggerEvent(FAULTY_HEATER_WAS_ON, idx, 0);
handleSwitchOffCommand(static_cast<heater::Switchers>(idx)); handleSwitchOffCommand(static_cast<heater::Switch>(idx));
continue; continue;
} }
} }
if (heaterVec[idx].cmdActive) { if (heaterVec[idx].cmdActive) {
switch (heaterVec[idx].action) { switch (heaterVec[idx].action) {
case SET_SWITCH_ON: case SET_SWITCH_ON:
handleSwitchOnCommand(static_cast<heater::Switchers>(idx)); handleSwitchOnCommand(static_cast<heater::Switch>(idx));
break; break;
case SET_SWITCH_OFF: case SET_SWITCH_OFF:
handleSwitchOffCommand(static_cast<heater::Switchers>(idx)); handleSwitchOffCommand(static_cast<heater::Switch>(idx));
break; break;
default: default:
sif::error << "HeaterHandler::handleActiveCommands: Invalid action commanded" sif::error << "HeaterHandler::handleActiveCommands: Invalid action commanded"
@ -236,7 +236,7 @@ void HeaterHandler::handleSwitchHandling() {
} }
} }
void HeaterHandler::handleSwitchOnCommand(heater::Switchers heaterIdx) { void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) {
ReturnValue_t result = returnvalue::OK; ReturnValue_t result = returnvalue::OK;
auto& heater = heaterVec.at(heaterIdx); auto& heater = heaterVec.at(heaterIdx);
if (waitForSwitchOff) { if (waitForSwitchOff) {
@ -307,7 +307,7 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switchers heaterIdx) {
} }
} }
void HeaterHandler::handleSwitchOffCommand(heater::Switchers 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 // Check whether switch is already off
@ -344,12 +344,12 @@ void HeaterHandler::handleSwitchOffCommand(heater::Switchers heaterIdx) {
heater.cmdActive = false; heater.cmdActive = false;
} }
HeaterHandler::SwitchState HeaterHandler::getSwitchState(heater::Switchers switchNr) const { HeaterHandler::SwitchState HeaterHandler::getSwitchState(heater::Switch switchNr) const {
MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);
return heaterVec.at(switchNr).switchState; return heaterVec.at(switchNr).switchState;
} }
ReturnValue_t HeaterHandler::switchHeater(heater::Switchers heater, SwitchState switchState) { ReturnValue_t HeaterHandler::switchHeater(heater::Switch heater, SwitchState switchState) {
if (switchState == SwitchState::ON) { if (switchState == SwitchState::ON) {
return sendSwitchCommand(heater, PowerSwitchIF::SWITCH_ON); return sendSwitchCommand(heater, PowerSwitchIF::SWITCH_ON);
} else if (switchState == SwitchState::OFF) { } else if (switchState == SwitchState::OFF) {
@ -428,7 +428,7 @@ ReturnValue_t HeaterHandler::getSwitchState(uint8_t switchNr) const {
if (switchNr > 7) { if (switchNr > 7) {
return returnvalue::FAILED; return returnvalue::FAILED;
} }
if (getSwitchState(static_cast<heater::Switchers>(switchNr)) == SwitchState::ON) { if (getSwitchState(static_cast<heater::Switch>(switchNr)) == SwitchState::ON) {
return PowerSwitchIF::SWITCH_ON; return PowerSwitchIF::SWITCH_ON;
} }
return PowerSwitchIF::SWITCH_OFF; return PowerSwitchIF::SWITCH_OFF;
@ -438,7 +438,7 @@ ReturnValue_t HeaterHandler::getFuseState(uint8_t fuseNr) const { return 0; }
uint32_t HeaterHandler::getSwitchDelayMs(void) const { return 2000; } uint32_t HeaterHandler::getSwitchDelayMs(void) const { return 2000; }
HasHealthIF::HealthState HeaterHandler::getHealth(heater::Switchers heater) { HasHealthIF::HealthState HeaterHandler::getHealth(heater::Switch heater) {
auto* healthDev = heaterVec.at(heater).healthDevice; auto* healthDev = heaterVec.at(heater).healthDevice;
if (healthDev != nullptr) { if (healthDev != nullptr) {
MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX);

View File

@ -20,8 +20,8 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "devices/heaterSwitcherList.h"
#include "events/subsystemIdRanges.h" #include "events/subsystemIdRanges.h"
#include "mission/tcs/defs.h"
#include "returnvalues/classIds.h" #include "returnvalues/classIds.h"
class PowerSwitchIF; class PowerSwitchIF;
@ -75,8 +75,8 @@ class HeaterHandler : public ExecutableObjectIF,
protected: protected:
enum SwitchAction : uint8_t { SET_SWITCH_OFF, SET_SWITCH_ON, NONE }; enum SwitchAction : uint8_t { SET_SWITCH_OFF, SET_SWITCH_ON, NONE };
ReturnValue_t switchHeater(heater::Switchers heater, SwitchState switchState); ReturnValue_t switchHeater(heater::Switch heater, SwitchState switchState);
HasHealthIF::HealthState getHealth(heater::Switchers heater); HasHealthIF::HealthState getHealth(heater::Switch heater);
ReturnValue_t performOperation(uint8_t operationCode = 0) override; ReturnValue_t performOperation(uint8_t operationCode = 0) override;
@ -174,7 +174,7 @@ class HeaterHandler : public ExecutableObjectIF,
* @brief Returns the state of a switch (ON - true, or OFF - false). * @brief Returns the state of a switch (ON - true, or OFF - false).
* @param switchNr The number of the switch to check. * @param switchNr The number of the switch to check.
*/ */
SwitchState getSwitchState(heater::Switchers switchNr) const; SwitchState getSwitchState(heater::Switch switchNr) const;
/** /**
* @brief This function runs commands waiting for execution. * @brief This function runs commands waiting for execution.
@ -198,9 +198,9 @@ class HeaterHandler : public ExecutableObjectIF,
const HasModesIF& getModeIF() const override; const HasModesIF& getModeIF() const override;
ModeTreeChildIF& getModeTreeChildIF() override; ModeTreeChildIF& getModeTreeChildIF() override;
void handleSwitchOnCommand(heater::Switchers heaterIdx); void handleSwitchOnCommand(heater::Switch heaterIdx);
void handleSwitchOffCommand(heater::Switchers heaterIdx); void handleSwitchOffCommand(heater::Switch heaterIdx);
/** /**
* @brief Checks if all switches are off. * @brief Checks if all switches are off.

View File

@ -1,6 +1,8 @@
#include <fsfw/datapool/PoolReadGuard.h> #include <fsfw/datapool/PoolReadGuard.h>
#include <mission/tcs/Max31865EiveHandler.h> #include <mission/tcs/Max31865EiveHandler.h>
#include "fsfw/thermal/tcsDefinitions.h"
Max31865EiveHandler::Max31865EiveHandler(object_id_t objectId, object_id_t comIF, Max31865EiveHandler::Max31865EiveHandler(object_id_t objectId, object_id_t comIF,
CookieIF* comCookie) CookieIF* comCookie)
: DeviceHandlerBase(objectId, comIF, comCookie, nullptr), : DeviceHandlerBase(objectId, comIF, comCookie, nullptr),
@ -152,6 +154,15 @@ ReturnValue_t Max31865EiveHandler::interpretDeviceReply(DeviceCommandId_t id,
transitionOk = true; transitionOk = true;
return returnvalue::OK; return returnvalue::OK;
} }
// If the 15 received bits are all ones, this is considered a case where the device does not
// work because it does not drive the MISO line. This can happens if the sensor is broken
// or off.
if (exchangeStruct.adcCode == 0x7fff) {
PoolReadGuard pg(&sensorDataset);
sensorDataset.temperatureCelcius.setValid(false);
sensorDataset.temperatureCelcius = thermal::INVALID_TEMPERATURE;
return returnvalue::FAILED;
}
// Calculate resistance // Calculate resistance
float rtdValue = exchangeStruct.adcCode * EiveMax31855::RTD_RREF_PT1000 / INT16_MAX; float rtdValue = exchangeStruct.adcCode * EiveMax31855::RTD_RREF_PT1000 / INT16_MAX;

View File

@ -1,10 +1,10 @@
#ifndef FSFWCONFIG_DEVICES_HEATERSWITCHERLIST_H_ #pragma once
#define FSFWCONFIG_DEVICES_HEATERSWITCHERLIST_H_
#include <atomic>
#include <cstdint> #include <cstdint>
namespace heater { namespace heater {
enum Switchers : uint8_t { enum Switch : uint8_t {
HEATER_0_OBC_BRD, HEATER_0_OBC_BRD,
HEATER_1_PLOC_PROC_BRD, HEATER_1_PLOC_PROC_BRD,
HEATER_2_ACS_BRD, HEATER_2_ACS_BRD,
@ -17,4 +17,8 @@ enum Switchers : uint8_t {
}; };
} }
#endif /* FSFWCONFIG_DEVICES_HEATERSWITCHERLIST_H_ */ namespace tcs {
extern std::atomic_bool TCS_BOARD_SHORTLY_UNAVAILABLE;
}

View File

@ -11,7 +11,7 @@ fi
init_dir=$(pwd) init_dir=$(pwd)
obsw_root="" obsw_root=""
q7s_yocto_dir="q7s-yocto" q7s_yocto_dir="yocto"
q7s_package_path="q7s-package/${q7s_yocto_dir}" q7s_package_path="q7s-package/${q7s_yocto_dir}"
obsw_version_filename="obsw_version.txt" obsw_version_filename="obsw_version.txt"

View File

@ -4,7 +4,8 @@ endif()
# Dependency on proprietary library # Dependency on proprietary library
if(TGT_BSP MATCHES "arm/q7s") if(TGT_BSP MATCHES "arm/q7s")
add_subdirectory(arcsec_star_tracker) # Only add required folder for wire library.
add_subdirectory(sagittactl/wire)
endif() endif()
add_subdirectory(rapidcsv) add_subdirectory(rapidcsv)

@ -1 +0,0 @@
Subproject commit 2823952e0902726e6e35dd7c159761f76bf7e505

1
thirdparty/sagittactl vendored Submodule

@ -0,0 +1 @@
Subproject commit 64332216c193fa6483258060b53fc2842c08dcf4

2
tmtc

@ -1 +1 @@
Subproject commit 43b530cdb7dfe6774962bf4b8a880e1c9a6e6580 Subproject commit f8da9cff7c5d6d6bdd483f90ccefb67b2d1e11a4

View File

@ -15,6 +15,7 @@
TEST_CASE("Thermal Controller", "[ThermalController]") { TEST_CASE("Thermal Controller", "[ThermalController]") {
const object_id_t THERMAL_CONTROLLER_ID = 0x123; const object_id_t THERMAL_CONTROLLER_ID = 0x123;
std::atomic_bool tcsBrdShortlyUnavailable = false;
TemperatureSensorInserter::Max31865DummyMap map0; TemperatureSensorInserter::Max31865DummyMap map0;
TemperatureSensorInserter::Tmp1075DummyMap map1; TemperatureSensorInserter::Tmp1075DummyMap map1;
@ -29,7 +30,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") {
// testEnvironment::initialize(); // testEnvironment::initialize();
ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler); ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler, tcsBrdShortlyUnavailable);
ReturnValue_t result = controller.initialize(); ReturnValue_t result = controller.initialize();
REQUIRE(result == returnvalue::OK); REQUIRE(result == returnvalue::OK);
@ -46,8 +47,8 @@ TEST_CASE("Thermal Controller", "[ThermalController]") {
CommandMessage modeMessage; CommandMessage modeMessage;
ModeMessage::setModeMessage(&modeMessage, ModeMessage::CMD_MODE_COMMAND, ModeMessage::setModeMessage(&modeMessage, ModeMessage::CMD_MODE_COMMAND, HasModesIF::MODE_ON,
ControllerBase::MODE_NORMAL, HasModesIF::SUBMODE_NONE); HasModesIF::SUBMODE_NONE);
MessageQueueIF* commandQueue = MessageQueueIF* commandQueue =
QueueFactory::instance()->createMessageQueue(5, MessageQueueMessage::MAX_MESSAGE_SIZE); QueueFactory::instance()->createMessageQueue(5, MessageQueueMessage::MAX_MESSAGE_SIZE);
@ -57,7 +58,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") {
REQUIRE(controller.performOperation(0) == returnvalue::OK); REQUIRE(controller.performOperation(0) == returnvalue::OK);
REQUIRE(testEnvironment::eventManager->isEventInEventList( REQUIRE(testEnvironment::eventManager->isEventInEventList(
THERMAL_CONTROLLER_ID, HasModesIF::MODE_INFO, ControllerBase::MODE_NORMAL, THERMAL_CONTROLLER_ID, HasModesIF::MODE_INFO, HasModesIF::MODE_ON,
HasModesIF::SUBMODE_NONE) == true); HasModesIF::SUBMODE_NONE) == true);
QueueFactory::instance()->deleteMessageQueue(commandQueue); QueueFactory::instance()->deleteMessageQueue(commandQueue);