diff --git a/CHANGELOG.md b/CHANGELOG.md index bcbc7a62..c3965b2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ will consitute of a breaking change warranting a new major release: - 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. +- 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. 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 @@ -52,16 +53,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. 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. +- TCS controller now only has an OFF mode and an ON mode - The TCS controller pauses operations related to the TCS board assembly (reading sensors and the primary control loop) while a TCS board recovery is on-going. - -## Changed - - Allow specifying custom OBSW update filename. This allowed keeping a cleaner file structure where each update has a name including the version - The files extracted during an update process are deleted after the update was performed to keep the update directory cleaner. +## 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 - eive-tmtc: v2.22.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d73475d..f2ac6db1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,9 @@ set(OBSW_ADD_SUN_SENSORS set(OBSW_ADD_SUS_BOARD_ASS ${INIT_VAL} 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 ${INIT_VAL} CACHE STRING "Add ACS board module") diff --git a/bsp_hosted/scheduling.cpp b/bsp_hosted/scheduling.cpp index 600a94e1..0a5f1c35 100644 --- a/bsp_hosted/scheduling.cpp +++ b/bsp_hosted/scheduling.cpp @@ -151,7 +151,6 @@ void scheduling::initTasks() { if (result != returnvalue::OK) { scheduling::printAddObjectError("Core controller dummy", objects::CORE_CONTROLLER); } - result = thermalTask->addComponent(objects::THERMAL_CONTROLLER); if (result != returnvalue::OK) { scheduling::printAddObjectError("THERMAL_CONTROLLER", objects::THERMAL_CONTROLLER); diff --git a/bsp_q7s/OBSWConfig.h.in b/bsp_q7s/OBSWConfig.h.in index 272467b5..3502a7bb 100644 --- a/bsp_q7s/OBSWConfig.h.in +++ b/bsp_q7s/OBSWConfig.h.in @@ -43,6 +43,9 @@ #define OBSW_ADD_PL_PCDU @OBSW_ADD_PL_PCDU@ #define OBSW_ADD_SYRLINKS @OBSW_ADD_SYRLINKS@ #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 #define OBSW_TM_TO_PTME @OBSW_TM_TO_PTME@ // Set to 1 if telecommands are received via the PDEC IP Core diff --git a/bsp_q7s/core/scheduling.cpp b/bsp_q7s/core/scheduling.cpp index 3f7000c7..f2cd8229 100644 --- a/bsp_q7s/core/scheduling.cpp +++ b/bsp_q7s/core/scheduling.cpp @@ -302,6 +302,9 @@ void scheduling::initTasks() { PeriodicTaskIF* tcsSystemTask = factory->createPeriodicTask( "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); result = tcsSystemTask->addComponent(objects::TCS_SUBSYSTEM); if (result != returnvalue::OK) { @@ -317,12 +320,10 @@ void scheduling::initTasks() { scheduling::printAddObjectError("THERMAL_CONTROLLER", objects::THERMAL_CONTROLLER); } #endif -#if OBSW_ADD_HEATERS == 1 result = tcsSystemTask->addComponent(objects::HEATER_HANDLER); if (result != returnvalue::OK) { scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER); } -#endif #if OBSW_ADD_SYRLINKS == 1 PeriodicTaskIF* syrlinksCom = factory->createPeriodicTask( diff --git a/common/config/devices/heaterSwitcherList.h b/common/config/devices/heaterSwitcherList.h deleted file mode 100644 index 8f91385d..00000000 --- a/common/config/devices/heaterSwitcherList.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef FSFWCONFIG_DEVICES_HEATERSWITCHERLIST_H_ -#define FSFWCONFIG_DEVICES_HEATERSWITCHERLIST_H_ - -#include - -namespace heater { -enum Switchers : uint8_t { - HEATER_0_OBC_BRD, - HEATER_1_PLOC_PROC_BRD, - HEATER_2_ACS_BRD, - HEATER_3_PCDU_PDU, - HEATER_4_CAMERA, - HEATER_5_STR, - HEATER_6_DRO, - HEATER_7_S_BAND, - NUMBER_OF_SWITCHES -}; -} - -#endif /* FSFWCONFIG_DEVICES_HEATERSWITCHERLIST_H_ */ diff --git a/dummies/TemperatureSensorInserter.cpp b/dummies/TemperatureSensorInserter.cpp index 3d268953..b6f46fb8 100644 --- a/dummies/TemperatureSensorInserter.cpp +++ b/dummies/TemperatureSensorInserter.cpp @@ -1,5 +1,6 @@ #include "TemperatureSensorInserter.h" +#include #include #include @@ -14,10 +15,7 @@ TemperatureSensorInserter::TemperatureSensorInserter(object_id_t objectId, tmp1075DummyMap(std::move(tempTmpSensorDummies_)) {} ReturnValue_t TemperatureSensorInserter::initialize() { - if (performTest) { - if (testCase == TestCase::COOL_SYRLINKS) { - } - } + testCase = TestCase::COLD_STR_CONSECUTIVE; return returnvalue::OK; } @@ -33,35 +31,72 @@ ReturnValue_t TemperatureSensorInserter::performOperation(uint8_t opCode) { tempsWereInitialized = true; } - if (cycles == 10) { - max31865DummyMap[objects::RTD_9_IC12_HPA]->setTemperature(-100, true); - max31865DummyMap[objects::RTD_11_IC14_MPA]->setTemperature(-100, true); + switch (testCase) { + case (TestCase::NONE): { + 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 tempSyrlinksBasebandBoard = + lp_var_t(objects::SYRLINKS_HANDLER, syrlinks::TEMP_BASEBAND_BOARD); + PoolReadGuard pg(&tempSyrlinksBasebandBoard); + tempSyrlinksBasebandBoard.value = -50; + } + if (cycles == 30) { + lp_var_t tempSyrlinksBasebandBoard = + lp_var_t(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++; return returnvalue::OK; } diff --git a/dummies/TemperatureSensorInserter.h b/dummies/TemperatureSensorInserter.h index 0c05cf34..675bcd91 100644 --- a/dummies/TemperatureSensorInserter.h +++ b/dummies/TemperatureSensorInserter.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "Max31865Dummy.h" @@ -22,11 +23,18 @@ class TemperatureSensorInserter : public ExecutableObjectIF, public SystemObject private: Max31865DummyMap max31865DummyMap; 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; uint32_t cycles = 0; bool tempsWereInitialized = false; - bool performTest = false; TestCase testCase = TestCase::NONE; // void noise(); diff --git a/dummies/helperFactory.cpp b/dummies/helperFactory.cpp index f2323bee..5a7d1d5b 100644 --- a/dummies/helperFactory.cpp +++ b/dummies/helperFactory.cpp @@ -194,9 +194,11 @@ void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpio tmpSensorDummies.emplace( objects::TMP1075_HANDLER_PLPCDU_0, new Tmp1075Dummy(objects::TMP1075_HANDLER_PLPCDU_0, objects::DUMMY_COM_IF, comCookieDummy)); - tmpSensorDummies.emplace( - objects::TMP1075_HANDLER_PLPCDU_1, - new Tmp1075Dummy(objects::TMP1075_HANDLER_PLPCDU_1, objects::DUMMY_COM_IF, comCookieDummy)); + // damaged. + // tmpSensorDummies.emplace( + // objects::TMP1075_HANDLER_PLPCDU_1, + // new Tmp1075Dummy(objects::TMP1075_HANDLER_PLPCDU_1, objects::DUMMY_COM_IF, + // comCookieDummy)); tmpSensorDummies.emplace( objects::TMP1075_HANDLER_IF_BOARD, new Tmp1075Dummy(objects::TMP1075_HANDLER_IF_BOARD, objects::DUMMY_COM_IF, comCookieDummy)); diff --git a/mission/controller/ThermalController.cpp b/mission/controller/ThermalController.cpp index fc2da4ef..b12088ae 100644 --- a/mission/controller/ThermalController.cpp +++ b/mission/controller/ThermalController.cpp @@ -17,7 +17,7 @@ #include // Enabling this should trigger a special event which in turn should trigger a system reaction. -#define LOWER_SYRLINKS_UPPER_LIMITS 0 +#define LOWER_SYRLINKS_UPPER_LIMITS 1 #define LOWER_EBAND_UPPER_LIMITS 0 #define LOWER_PLOC_UPPER_LIMITS 0 @@ -170,26 +170,31 @@ void ThermalController::performControlOperation() { } } - if (transitionToOff) { - for (const auto& switchState : heaterSwitchStateArray) { - if (switchState != HeaterHandler::SwitchState::OFF) { - transitionToOffCycles++; - // if heater still ON after 10 cycles, switch OFF again - if (transitionToOffCycles == 10) { - for (uint8_t i = 0; i < heater::Switchers::NUMBER_OF_SWITCHES; i++) { - heaterHandler.switchHeater(static_cast(i), - HeaterHandler::SwitchState::OFF); - } + cycles++; + if (transitionWhenHeatersOff) { + bool allSwitchersOff = true; + for (size_t idx = 0; idx < heaterSwitchStateArray.size(); idx++) { + if (heaterSwitchStateArray[idx] != HeaterHandler::SwitchState::OFF) { + allSwitchersOff = false; + // if heater still ON after 3 cycles, switch OFF again + if (transitionWhenHeatersOffCycles == 3) { + heaterHandler.switchHeater(static_cast(idx), + HeaterHandler::SwitchState::OFF); triggerEvent(tcsCtrl::HEATER_NOT_OFF_FOR_OFF_MODE); } - return; } - setMode(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); } - cycles++; } ReturnValue_t ThermalController::initializeLocalDataPool(localpool::DataPool& localDataPoolMap, @@ -292,12 +297,18 @@ LocalPoolDataSetBase* ThermalController::getDataSetHandle(sid_t sid) { ReturnValue_t ThermalController::checkModeCommand(Mode_t mode, Submode_t submode, 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) { return INVALID_SUBMODE; } - if ((mode != MODE_OFF) && (mode != MODE_ON) && (mode != MODE_NORMAL)) { - return INVALID_MODE; - } return returnvalue::OK; } @@ -976,8 +987,8 @@ void ThermalController::copyDevices() { } void ThermalController::ctrlAcsBoard() { - heater::Switchers switchNr = heater::HEATER_2_ACS_BRD; - heater::Switchers redSwitchNr = heater::HEATER_0_OBC_BRD; + heater::Switch switchNr = heater::HEATER_2_ACS_BRD; + heater::Switch redSwitchNr = heater::HEATER_0_OBC_BRD; // A side thermalComponent = ACS_BOARD; @@ -1023,7 +1034,9 @@ void ThermalController::ctrlAcsBoard() { } else { if (chooseHeater(switchNr, redSwitchNr)) { if (heaterHandler.getSwitchState(switchNr)) { - heaterHandler.switchHeater(switchNr, HeaterHandler::SwitchState::OFF); + if (submode != SUBMODE_NO_HEATER_CTRL) { + heaterSwitchHelper(switchNr, HeaterHandler::SwitchState::OFF, thermalComponent); + } } } } @@ -1268,8 +1281,8 @@ void ThermalController::ctrlPcduP60Board() { void ThermalController::ctrlPcduAcu() { thermalComponent = PCDUACU; - heater::Switchers switchNr = heater::HEATER_3_PCDU_PDU; - heater::Switchers redSwitchNr = heater::HEATER_2_ACS_BRD; + heater::Switch switchNr = heater::HEATER_3_PCDU_PDU; + heater::Switch redSwitchNr = heater::HEATER_2_ACS_BRD; if (chooseHeater(switchNr, redSwitchNr)) { bool sensorTempAvailable = true; @@ -1557,13 +1570,16 @@ void ThermalController::performThermalModuleCtrl(const HeaterSwitchStates& heate void ThermalController::ctrlComponentTemperature(HeaterContext& htrCtx) { if (selectAndReadSensorTemp(htrCtx)) { if (chooseHeater(htrCtx.switchNr, htrCtx.redSwitchNr)) { + // Core loop for a thermal component, after sensors and heaters were selected. checkLimitsAndCtrlHeater(htrCtx); } } 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 (heaterHandler.getSwitchState(htrCtx.switchNr)) { - heaterHandler.switchHeater(htrCtx.switchNr, HeaterHandler::SwitchState::OFF); + if (heaterCtrlAllowed() and + (heaterHandler.getSwitchState(htrCtx.switchNr) == HeaterHandler::SwitchState::ON)) { + heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::OFF, thermalComponent); } } } @@ -1593,7 +1609,7 @@ bool ThermalController::selectAndReadSensorTemp(HeaterContext& htrCtx) { return false; } -bool ThermalController::chooseHeater(heater::Switchers& switchNr, heater::Switchers redSwitchNr) { +bool ThermalController::chooseHeater(heater::Switch& switchNr, heater::Switch redSwitchNr) { bool heaterAvailable = true; if (heaterHandler.getHealth(switchNr) != HasHealthIF::HEALTHY) { @@ -1611,15 +1627,18 @@ bool ThermalController::chooseHeater(heater::Switchers& switchNr, heater::Switch } void ThermalController::heaterCtrlTempTooHighHandler(HeaterContext& htrCtx, const char* whatLimit) { + if (not heaterCtrlAllowed()) { + return; + } if (htrCtx.switchState == HeaterHandler::SwitchState::ON) { sif::info << "TCS: Component " << static_cast(thermalComponent) << " too warm, above " << 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].target = HeaterHandler::SwitchState::OFF; } 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].target = HeaterHandler::SwitchState::OFF; } @@ -1634,43 +1653,44 @@ void ThermalController::checkLimitsAndCtrlHeater(HeaterContext& htrCtx) { if (heaterStates[htrCtx.switchNr].switchTransition) { htrCtx.doHeaterHandling = false; heaterCtrlCheckUpperLimits(htrCtx); - } else { - // Heater off - htrCtx.switchState = heaterHandler.getSwitchState(htrCtx.switchNr); - if (htrCtx.switchState == HeaterHandler::SwitchState::OFF) { - if (sensorTemp < htrCtx.tempLimit.opLowerLimit) { - heaterHandler.switchHeater(htrCtx.switchNr, HeaterHandler::SwitchState::ON); - sif::info << "ThermalController::checkLimitsAndCtrlHeater: Heater " - << static_cast(thermalComponent) << " ON" << std::endl; + return; + } + + htrCtx.switchState = heaterHandler.getSwitchState(htrCtx.switchNr); + // Heater off + if (htrCtx.switchState == HeaterHandler::SwitchState::OFF) { + if (sensorTemp < htrCtx.tempLimit.opLowerLimit and heaterCtrlAllowed()) { + sif::info << "TCS: Heater " << static_cast(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(thermalComponent) << " OFF" << std::endl; + heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::OFF, thermalComponent); heaterStates[htrCtx.switchNr].switchTransition = true; - thermalStates[thermalComponent].heating = true; - heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::ON; - } else { - thermalStates[thermalComponent].heating = false; + heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::OFF; } - heaterCtrlCheckUpperLimits(htrCtx); - // Heater on - } else if (heaterHandler.getSwitchState(htrCtx.switchNr) == 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) { - heaterHandler.switchHeater(htrCtx.switchNr, HeaterHandler::SwitchState::OFF); - sif::info << "ThermalController::checkLimitsAndCtrlHeater: Heater " - << static_cast(thermalComponent) << " OFF" << std::endl; - 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"); - } - } + return; + } + // 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"); } } } @@ -1702,8 +1722,8 @@ void ThermalController::resetSensorsArray() { } thermalComponent = NONE; } + void ThermalController::heaterTransitionControl(const HeaterSwitchStates& currentHeaterStates) { - // TODO: Test for (unsigned i = 0; i < 7; i++) { if (heaterStates[i].switchTransition) { if (currentHeaterStates[i] == heaterStates[i].target) { @@ -1727,11 +1747,12 @@ uint32_t ThermalController::tempFloatToU32() const { return tempRaw; } -void ThermalController::setMode(Mode_t mode) { +void ThermalController::setMode(Mode_t mode, Submode_t submode) { if (mode == MODE_OFF) { - transitionToOff = false; + transitionWhenHeatersOff = false; } this->mode = mode; + this->submode = submode; modeHelper.modeChanged(mode, submode); announceMode(false); } @@ -1746,6 +1767,43 @@ bool ThermalController::tooHotHandler(object_id_t object, bool& oneShotFlag) { 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(¤tTime); + 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(¤tTime); + size_t idx = 0; + for (; idx < heater::Switch::NUMBER_OF_SWITCHES; idx++) { + heaterHandler.switchHeater(static_cast(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) { // Clear the one shot flag is the component is in acceptable temperature range. if (not tooHotHandler(object, oneShotFlag) and not componentAboveUpperLimit) { @@ -1755,14 +1813,15 @@ void ThermalController::tooHotHandlerWhichClearsOneShotFlag(object_id_t object, void ThermalController::startTransition(Mode_t mode_, Submode_t submode_) { triggerEvent(CHANGING_MODE, mode_, submode_); - if (mode_ == MODE_OFF) { - for (uint8_t i = 0; i < heater::Switchers::NUMBER_OF_SWITCHES; i++) { - heaterHandler.switchHeater(static_cast(i), - HeaterHandler::SwitchState::OFF); - } - transitionToOff = true; - transitionToOffCycles = 0; + // For MODE_OFF and the no heater control submode, we command all switches to off before + // completing the transition. This ensures a consistent state when commanding these modes. + if ((mode_ == MODE_OFF) or ((mode_ == MODE_ON) and (submode_ == SUBMODE_NO_HEATER_CTRL))) { + heaterSwitchHelperAllOff(); + transitionWhenHeatersOff = true; + targetMode = mode_; + targetSubmode = submode_; + transitionWhenHeatersOffCycles = 0; } else { - setMode(mode_); + setMode(mode_, submode_); } } diff --git a/mission/controller/ThermalController.h b/mission/controller/ThermalController.h index 54b05216..7aa05b1e 100644 --- a/mission/controller/ThermalController.h +++ b/mission/controller/ThermalController.h @@ -48,8 +48,13 @@ struct TempLimits { struct ThermalState { uint8_t errorCounter; - bool heating; - uint32_t heaterStartTime; + // Is heating on for that thermal module? + 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 { @@ -90,6 +95,8 @@ enum ThermalComponents : uint8_t { class ThermalController : public ExtendedControllerBase { public: + static constexpr uint8_t SUBMODE_NO_HEATER_CTRL = 1; + static const uint16_t INVALID_TEMPERATURE = 999; static const uint8_t NUMBER_OF_SENSORS = 16; static constexpr int16_t SANITY_LIMIT_LOWER_TEMP = -80; @@ -103,13 +110,13 @@ class ThermalController : public ExtendedControllerBase { protected: struct HeaterContext { public: - HeaterContext(heater::Switchers switchNr, heater::Switchers redundantSwitchNr, + HeaterContext(heater::Switch switchNr, heater::Switch redundantSwitchNr, const TempLimits& tempLimit) : switchNr(switchNr), redSwitchNr(redundantSwitchNr), tempLimit(tempLimit) {} bool doHeaterHandling = true; - heater::Switchers switchNr; + heater::Switch switchNr; HeaterHandler::SwitchState switchState = HeaterHandler::SwitchState::OFF; - heater::Switchers redSwitchNr; + heater::Switch redSwitchNr; const TempLimits& tempLimit; }; @@ -263,11 +270,13 @@ class ThermalController : public ExtendedControllerBase { bool strTooHotFlag = false; bool rwTooHotFlag = false; - bool transitionToOff = false; - uint32_t transitionToOffCycles = 0; + bool transitionWhenHeatersOff = false; + uint32_t transitionWhenHeatersOffCycles = 0; + Mode_t targetMode = MODE_OFF; + Submode_t targetSubmode = SUBMODE_NONE; uint32_t cycles = 0; - std::array thermalStates{}; - std::array heaterStates{}; + std::array thermalStates{}; + std::array heaterStates{}; // Initial delay to make sure all pool variables have been initialized their owners. // Also, wait for system initialization to complete. @@ -292,6 +301,9 @@ class ThermalController : public ExtendedControllerBase { void startTransition(Mode_t mode, Submode_t submode) override; + bool heaterCtrlAllowed() const; + void resetThermalStates(); + void resetSensorsArray(); void copySensors(); void copySus(); @@ -302,9 +314,13 @@ class ThermalController : public ExtendedControllerBase { bool heaterCtrlCheckUpperLimits(HeaterContext& heaterContext); 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); + void heaterSwitchHelperAllOff(); + void heaterSwitchHelper(heater::Switch switchNr, HeaterHandler::SwitchState state, + unsigned componentIdx); + void ctrlAcsBoard(); void ctrlMgt(); void ctrlRw(); @@ -329,7 +345,7 @@ class ThermalController : public ExtendedControllerBase { void ctrlMpa(); void ctrlScexBoard(); void heaterTransitionControl(const HeaterSwitchStates& currentHeaterStates); - void setMode(Mode_t mode); + void setMode(Mode_t mode, Submode_t submode); uint32_t tempFloatToU32() const; bool tooHotHandler(object_id_t object, bool& oneShotFlag); void tooHotHandlerWhichClearsOneShotFlag(object_id_t object, bool& oneShotFlag); diff --git a/mission/controller/tcsDefs.h b/mission/controller/tcsDefs.h index e187091b..03aa8ffe 100644 --- a/mission/controller/tcsDefs.h +++ b/mission/controller/tcsDefs.h @@ -4,8 +4,8 @@ #include #include -#include "devices/heaterSwitcherList.h" #include "eive/eventSubsystemIds.h" +#include "mission/tcs/defs.h" namespace tcsCtrl { diff --git a/mission/system/tree/tcsModeTree.cpp b/mission/system/tree/tcsModeTree.cpp index 2cdb5052..6cc57c6f 100644 --- a/mission/system/tree/tcsModeTree.cpp +++ b/mission/system/tree/tcsModeTree.cpp @@ -111,7 +111,7 @@ void buildNormalSequence(Subsystem& ss, ModeListEntry& eh) { ctxc); // 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)), ctxc); diff --git a/mission/tcs/HeaterHandler.cpp b/mission/tcs/HeaterHandler.cpp index 9105e380..630dc92c 100644 --- a/mission/tcs/HeaterHandler.cpp +++ b/mission/tcs/HeaterHandler.cpp @@ -215,17 +215,17 @@ void HeaterHandler::handleSwitchHandling() { heaterVec[idx].cmdActive = true; heaterVec[idx].action = SET_SWITCH_OFF; triggerEvent(FAULTY_HEATER_WAS_ON, idx, 0); - handleSwitchOffCommand(static_cast(idx)); + handleSwitchOffCommand(static_cast(idx)); continue; } } if (heaterVec[idx].cmdActive) { switch (heaterVec[idx].action) { case SET_SWITCH_ON: - handleSwitchOnCommand(static_cast(idx)); + handleSwitchOnCommand(static_cast(idx)); break; case SET_SWITCH_OFF: - handleSwitchOffCommand(static_cast(idx)); + handleSwitchOffCommand(static_cast(idx)); break; default: 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; auto& heater = heaterVec.at(heaterIdx); 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; auto& heater = heaterVec.at(heaterIdx); // Check whether switch is already off @@ -344,12 +344,12 @@ void HeaterHandler::handleSwitchOffCommand(heater::Switchers heaterIdx) { 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); 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) { return sendSwitchCommand(heater, PowerSwitchIF::SWITCH_ON); } else if (switchState == SwitchState::OFF) { @@ -428,7 +428,7 @@ ReturnValue_t HeaterHandler::getSwitchState(uint8_t switchNr) const { if (switchNr > 7) { return returnvalue::FAILED; } - if (getSwitchState(static_cast(switchNr)) == SwitchState::ON) { + if (getSwitchState(static_cast(switchNr)) == SwitchState::ON) { return PowerSwitchIF::SWITCH_ON; } 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; } -HasHealthIF::HealthState HeaterHandler::getHealth(heater::Switchers heater) { +HasHealthIF::HealthState HeaterHandler::getHealth(heater::Switch heater) { auto* healthDev = heaterVec.at(heater).healthDevice; if (healthDev != nullptr) { MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); diff --git a/mission/tcs/HeaterHandler.h b/mission/tcs/HeaterHandler.h index 5bbd85f4..c3ea4afa 100644 --- a/mission/tcs/HeaterHandler.h +++ b/mission/tcs/HeaterHandler.h @@ -20,8 +20,8 @@ #include #include -#include "devices/heaterSwitcherList.h" #include "events/subsystemIdRanges.h" +#include "mission/tcs/defs.h" #include "returnvalues/classIds.h" class PowerSwitchIF; @@ -75,8 +75,8 @@ class HeaterHandler : public ExecutableObjectIF, protected: enum SwitchAction : uint8_t { SET_SWITCH_OFF, SET_SWITCH_ON, NONE }; - ReturnValue_t switchHeater(heater::Switchers heater, SwitchState switchState); - HasHealthIF::HealthState getHealth(heater::Switchers heater); + ReturnValue_t switchHeater(heater::Switch heater, SwitchState switchState); + HasHealthIF::HealthState getHealth(heater::Switch heater); 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). * @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. @@ -198,9 +198,9 @@ class HeaterHandler : public ExecutableObjectIF, const HasModesIF& getModeIF() const 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. diff --git a/mission/tcs/defs.h b/mission/tcs/defs.h index df868360..cb1a73e4 100644 --- a/mission/tcs/defs.h +++ b/mission/tcs/defs.h @@ -1,6 +1,21 @@ #pragma once #include +#include + +namespace heater { +enum Switch : uint8_t { + HEATER_0_OBC_BRD, + HEATER_1_PLOC_PROC_BRD, + HEATER_2_ACS_BRD, + HEATER_3_PCDU_PDU, + HEATER_4_CAMERA, + HEATER_5_STR, + HEATER_6_DRO, + HEATER_7_S_BAND, + NUMBER_OF_SWITCHES +}; +} namespace tcs { diff --git a/unittest/controller/testThermalController.cpp b/unittest/controller/testThermalController.cpp index dbe86949..0e12d94d 100644 --- a/unittest/controller/testThermalController.cpp +++ b/unittest/controller/testThermalController.cpp @@ -48,7 +48,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") { CommandMessage modeMessage; ModeMessage::setModeMessage(&modeMessage, ModeMessage::CMD_MODE_COMMAND, - ControllerBase::MODE_NORMAL, HasModesIF::SUBMODE_NONE); + HasModesIF::MODE_ON, HasModesIF::SUBMODE_NONE); MessageQueueIF* commandQueue = QueueFactory::instance()->createMessageQueue(5, MessageQueueMessage::MAX_MESSAGE_SIZE); @@ -58,7 +58,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") { REQUIRE(controller.performOperation(0) == returnvalue::OK); 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); QueueFactory::instance()->deleteMessageQueue(commandQueue);