diff --git a/.gitmodules b/.gitmodules index 76fb8527..e1ea03a4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,9 +10,6 @@ [submodule "thirdparty/lwgps"] path = thirdparty/lwgps 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"] path = thirdparty/json url = https://github.com/nlohmann/json.git @@ -22,3 +19,6 @@ [submodule "thirdparty/gomspace-sw"] path = thirdparty/gomspace-sw 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 diff --git a/CHANGELOG.md b/CHANGELOG.md index dc5bfc7b..7df62f43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,14 +16,47 @@ will consitute of a breaking change warranting a new major release: # [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 - 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 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 @@ -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. 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. - -## Changed - +- 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. - 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 7989ff35..71a94ff9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ cmake_minimum_required(VERSION 3.13) set(OBSW_VERSION_MAJOR 1) set(OBSW_VERSION_MINOR 44) -set(OBSW_VERSION_REVISION 0) +set(OBSW_VERSION_REVISION 1) # set(CMAKE_VERBOSE TRUE) @@ -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") @@ -220,7 +223,7 @@ set(LIB_EIVE_MISSION_PATH mission) set(LIB_ETL_PATH ${THIRD_PARTY_FOLDER}/etl) set(LIB_CATCH2_PATH ${THIRD_PARTY_FOLDER}/Catch2) 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(FSFW_WARNING_SHADOW_LOCAL_GCC OFF) 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/CoreController.cpp b/bsp_q7s/core/CoreController.cpp index 554e5268..e1033943 100644 --- a/bsp_q7s/core/CoreController.cpp +++ b/bsp_q7s/core/CoreController.cpp @@ -9,7 +9,6 @@ #include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/timemanager/Stopwatch.h" #include "fsfw/version.h" -#include "mission/sysDefs.h" #include "watchdog/definitions.h" #if OBSW_ADD_TMTC_UDP_SERVER == 1 #include "fsfw/osal/common/UdpTmTcBridge.h" @@ -40,7 +39,8 @@ CoreController::CoreController(object_id_t objectId, bool enableHkSet) cmdRepliesSizes(128), opDivider5(5), opDivider10(10), - hkSet(this) { + hkSet(this), + paramHelper(this) { cmdExecutor.setRingBuffer(&cmdReplyBuf, &cmdRepliesSizes); try { sdcMan = SdCardManager::instance(); @@ -88,6 +88,10 @@ CoreController::CoreController(object_id_t objectId, bool enableHkSet) CoreController::~CoreController() {} ReturnValue_t CoreController::handleCommandMessage(CommandMessage *message) { + ReturnValue_t result = paramHelper.handleParameterMessage(message); + if (result == returnvalue::OK) { + return result; + } return ExtendedControllerBase::handleCommandMessage(message); } @@ -113,7 +117,7 @@ void CoreController::performControlOperation() { bool replyReceived = false; // 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) { - actionHelper.finish(true, successRecipient, core::EXECUTE_SHELL_CMD); + actionHelper.finish(true, successRecipient, core::EXECUTE_SHELL_CMD_BLOCKING); shellCmdIsExecuting = false; cmdReplyBuf.clear(); while (not cmdRepliesSizes.empty()) { @@ -154,6 +158,11 @@ ReturnValue_t CoreController::initialize() { << std::endl; } + result = paramHelper.initialize(); + if (result != returnvalue::OK) { + return result; + } + sdStateMachine(); 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 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(data[0]); + std::string unitName = std::string(reinterpret_cast(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): { if (size != 3) { 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 return actionReboot(data, size); } - case (EXECUTE_SHELL_CMD): { - std::string cmd = std::string(cmd, size); + case (EXECUTE_SHELL_CMD_BLOCKING): { + std::string cmdToExecute = std::string(reinterpret_cast(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(data), size); if (cmdExecutor.getCurrentState() == CommandExecutor::States::PENDING or shellCmdIsExecuting) { return HasActionsIF::IS_BUSY; } - cmdExecutor.load(cmd, false, false); + cmdExecutor.load(cmdToExecute, false, false); ReturnValue_t result = cmdExecutor.execute(); if (result != returnvalue::OK) { return result; @@ -1298,7 +1351,7 @@ void CoreController::performMountedSdCardOperations() { auto mountedSdCardOp = [&](sd::SdCard sdCard, std::string mntPoint) { if (not performOneShotSdCardOpsSwitch) { std::ostringstream path; - path << mntPoint << "/" << CONF_FOLDER; + path << mntPoint << "/" << core::CONF_FOLDER; std::error_code e; if (not std::filesystem::exists(path.str()), e) { bool created = std::filesystem::create_directory(path.str(), e); @@ -2124,7 +2177,38 @@ void CoreController::announceBootCounts() { totalBootCount & 0xffffffff); } +MessageQueueId_t CoreController::getCommandQueue() const { + return ExtendedControllerBase::getCommandQueue(); +} + bool CoreController::isNumber(const std::string &s) { return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end(); } + +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(newPrefSd)); + if (result != returnvalue::OK) { + return returnvalue::FAILED; + } + parameterWrapper->set(prefSdRaw); + return returnvalue::OK; +} diff --git a/bsp_q7s/core/CoreController.h b/bsp_q7s/core/CoreController.h index 22b1f6ff..c1f5e40d 100644 --- a/bsp_q7s/core/CoreController.h +++ b/bsp_q7s/core/CoreController.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include @@ -16,17 +18,11 @@ #include "bsp_q7s/fs/SdCardManager.h" #include "events/subsystemIdRanges.h" #include "fsfw/controller/ExtendedControllerBase.h" +#include "mission/sysDefs.h" class Timer; 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 { static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10; @@ -48,8 +44,10 @@ struct RebootFile { xsc::Copy mechanismNextCopy = xsc::Copy::NO_COPY; }; -class CoreController : public ExtendedControllerBase { +class CoreController : public ExtendedControllerBase, public ReceivesParameterMessagesIF { public: + enum ParamId : uint8_t { PREF_SD = 0, NUM_IDS }; + static xsc::Chip CURRENT_CHIP; 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 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 = - "/" + 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 = - "/" + 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 = - "/" + 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_1_MOUNT_DIR[] = "/tmp/mntupdate-xdi-qspi0-gold-rootfs"; @@ -152,6 +144,7 @@ class CoreController : public ExtendedControllerBase { SdCardManager* sdcMan = nullptr; MessageQueueIF* eventQueue = nullptr; + uint8_t prefSdRaw = sd::SdCard::SLOT_0; SdStates sdFsmState = SdStates::START; SdStates fsmStateAfterDelay = SdStates::IDLE; enum SdCfgMode { PASSIVE, COLD_REDUNDANT, HOT_REDUNDANT }; @@ -216,10 +209,16 @@ class CoreController : public ExtendedControllerBase { core::HkSet hkSet; + ParameterHelper paramHelper; + #if OBSW_SD_CARD_MUST_BE_ON == 1 bool remountAttemptFlag = true; #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, LocalDataPoolManager& poolManager) override; 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/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 404b640c..5a7d1d5b 100644 --- a/dummies/helperFactory.cpp +++ b/dummies/helperFactory.cpp @@ -40,6 +40,7 @@ #include "mission/system/com/comModeTree.h" #include "mission/system/tree/payloadModeTree.h" #include "mission/system/tree/tcsModeTree.h" +#include "mission/tcs/defs.h" void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpioIF) { new ComIFDummy(objects::DUMMY_COM_IF); @@ -193,16 +194,19 @@ 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)); new TemperatureSensorInserter(objects::THERMAL_TEMP_INSERTER, rtdSensorDummies, tmpSensorDummies); - TcsBoardAssembly* tcsBoardAssy = ObjectFactory::createTcsBoardAssy(pwrSwitcher); + TcsBoardAssembly* tcsBoardAssy = + ObjectFactory::createTcsBoardAssy(pwrSwitcher, tcs::TCS_BOARD_SHORTLY_UNAVAILABLE); for (auto& rtd : rtdSensorDummies) { rtd.second->connectModeTreeParent(*tcsBoardAssy); } diff --git a/fsfw b/fsfw index 285d327b..ffa2fa47 160000 --- a/fsfw +++ b/fsfw @@ -1 +1 @@ -Subproject commit 285d327b97514946f0714e477289f67ee8bd413f +Subproject commit ffa2fa477f105cc876264335d5b25fc9b174a181 diff --git a/linux/ObjectFactory.cpp b/linux/ObjectFactory.cpp index 66a3195b..72f30278 100644 --- a/linux/ObjectFactory.cpp +++ b/linux/ObjectFactory.cpp @@ -29,6 +29,7 @@ #include "mission/system/acs/acsModeTree.h" #include "mission/system/tree/payloadModeTree.h" #include "mission/system/tree/tcsModeTree.h" +#include "mission/tcs/defs.h" void ObjectFactory::createSunSensorComponents(GpioIF* gpioComIF, SpiComIF* spiComIF, PowerSwitchIF& pwrSwitcher, std::string spiDev, @@ -278,7 +279,8 @@ void ObjectFactory::createRtdComponents(std::string spiDev, GpioIF* gpioComIF, std::array rtds = {}; RtdFdir* rtdFdir = nullptr; - TcsBoardAssembly* tcsBoardAss = ObjectFactory::createTcsBoardAssy(*pwrSwitcher); + TcsBoardAssembly* tcsBoardAss = + ObjectFactory::createTcsBoardAssy(*pwrSwitcher, tcs::TCS_BOARD_SHORTLY_UNAVAILABLE); // Create special low level reader communication interface new Max31865RtdPolling(objects::SPI_RTD_COM_IF, comIF, gpioComIF); diff --git a/linux/acs/AcsBoardPolling.cpp b/linux/acs/AcsBoardPolling.cpp index b289ef70..bc3dff74 100644 --- a/linux/acs/AcsBoardPolling.cpp +++ b/linux/acs/AcsBoardPolling.cpp @@ -113,7 +113,8 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send if (req->mode != adis.mode) { if (req->mode == acs::SimpleSensorMode::NORMAL) { adis.type = req->type; - adis.countdown.setTimeout(adis1650x::START_UP_TIME); + // The initial countdown is handled by the device handler now. + // adis.countdown.setTimeout(adis1650x::START_UP_TIME); if (adis.type == adis1650x::Type::ADIS16507) { adis.ownReply.data.accelScaling = adis1650x::ACCELEROMETER_RANGE_16507; } else if (adis.type == adis1650x::Type::ADIS16505) { @@ -127,6 +128,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send adis.ownReply.cfgWasSet = false; adis.ownReply.dataWasSet = false; } + adis.replyResult = returnvalue::FAILED; adis.mode = req->mode; } return returnvalue::OK; @@ -145,6 +147,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send } else { gyro.ownReply.cfgWasSet = false; } + gyro.replyResult = returnvalue::FAILED; gyro.mode = req->mode; } return returnvalue::OK; @@ -163,6 +166,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send mgm.ownReply.dataWasSet = false; mgm.ownReply.temperatureWasSet = false; } + mgm.replyResult = returnvalue::FAILED; mgm.mode = req->mode; } return returnvalue::OK; @@ -180,6 +184,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send } else { mgm.ownReply.dataWasRead = false; } + mgm.replyResult = returnvalue::FAILED; mgm.mode = req->mode; } return returnvalue::OK; @@ -309,18 +314,18 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) { std::memcpy(cmdBuf.data() + 1, l3g.sensorCfg, 5); result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6); if (result != returnvalue::OK) { - l3g.replyResult = returnvalue::OK; + l3g.replyResult = result; } // Ignore useless reply and red config cmdBuf[0] = l3gd20h::CTRL_REG_1 | l3gd20h::AUTO_INCREMENT_MASK | l3gd20h::READ_MASK; std::memset(cmdBuf.data() + 1, 0, 5); result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6); if (result != returnvalue::OK) { - l3g.replyResult = returnvalue::OK; + l3g.replyResult = result; } result = spiComIF.readReceivedMessage(l3g.cookie, &rawReply, &dummy); if (result != returnvalue::OK) { - l3g.replyResult = returnvalue::OK; + l3g.replyResult = result; } MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); // Cross check configuration as verification that communication is working @@ -331,6 +336,7 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) { return; } } + l3g.replyResult = returnvalue::OK; l3g.performStartup = false; l3g.ownReply.cfgWasSet = true; l3g.ownReply.sensitivity = l3gd20h::ctrlReg4ToSensitivity(l3g.sensorCfg[3]); @@ -357,6 +363,7 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) { return; } } + l3g.replyResult = returnvalue::OK; l3g.ownReply.statusReg = rawReply[l3gd20h::STATUS_IDX]; l3g.ownReply.angVelocities[0] = (rawReply[l3gd20h::OUT_X_H] << 8) | rawReply[l3gd20h::OUT_X_L]; l3g.ownReply.angVelocities[1] = (rawReply[l3gd20h::OUT_Y_H] << 8) | rawReply[l3gd20h::OUT_Y_L]; @@ -443,20 +450,15 @@ ReturnValue_t AcsBoardPolling::readAdisCfg(SpiCookie& cookie, size_t transferLen void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) { ReturnValue_t result; acs::SimpleSensorMode mode = acs::SimpleSensorMode::OFF; - bool cdHasTimedOut = false; bool mustPerformStartup = false; { MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); mode = gyro.mode; - cdHasTimedOut = gyro.countdown.hasTimedOut(); mustPerformStartup = gyro.performStartup; } if (mode == acs::SimpleSensorMode::OFF) { return; } - if (not cdHasTimedOut) { - return; - } if (mustPerformStartup) { uint8_t regList[6]; // Read configuration @@ -495,6 +497,7 @@ void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) { gyro.ownReply.cfg.prodId = prodId; gyro.ownReply.data.sensitivity = adis1650x::rangMdlToSensitivity(gyro.ownReply.cfg.rangMdl); gyro.performStartup = false; + gyro.replyResult = returnvalue::OK; } // Read regular registers std::memcpy(cmdBuf.data(), adis1650x::BURST_READ_ENABLE.data(), @@ -533,6 +536,7 @@ void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) { } MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); + gyro.replyResult = returnvalue::OK; gyro.ownReply.dataWasSet = true; gyro.ownReply.cfg.diagStat = rawReply[2] << 8 | rawReply[3]; gyro.ownReply.data.angVelocities[0] = (rawReply[4] << 8) | rawReply[5]; @@ -590,6 +594,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) { } // Done here. We can always read back config and data during periodic handling mgm.performStartup = false; + mgm.replyResult = returnvalue::OK; } cmdBuf[0] = mgmLis3::readCommand(mgmLis3::CTRL_REG1, true); std::memset(cmdBuf.data() + 1, 0, mgmLis3::NR_OF_DATA_AND_CFG_REGISTERS); @@ -607,7 +612,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) { // Verify communication by re-checking config if (rawReply[1] != mgm.cfg[0] or rawReply[2] != mgm.cfg[1] or rawReply[3] != mgm.cfg[2] or rawReply[4] != mgm.cfg[3] or rawReply[5] != mgm.cfg[4]) { - mgm.replyResult = result; + mgm.replyResult = returnvalue::FAILED; return; } { @@ -634,6 +639,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) { return; } MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); + mgm.replyResult = returnvalue::OK; mgm.ownReply.temperatureWasSet = true; mgm.ownReply.temperatureRaw = (rawReply[2] << 8) | rawReply[1]; } @@ -704,6 +710,7 @@ void AcsBoardPolling::mgmRm3100Handler(MgmRm3100& mgm) { return; } mgm.performStartup = false; + mgm.replyResult = returnvalue::OK; } // Regular read operation cmdBuf[0] = mgmRm3100::MEASUREMENT_REG_START | mgmRm3100::READ_MASK; @@ -725,6 +732,7 @@ void AcsBoardPolling::mgmRm3100Handler(MgmRm3100& mgm) { mgm.ownReply.scaleFactors[idx] = 1.0 / mgmRm3100::DEFAULT_GAIN; } mgm.ownReply.dataWasRead = true; + mgm.replyResult = returnvalue::OK; // Bitshift trickery to account for 24 bit signed value. mgm.ownReply.mgmValuesRaw[0] = ((rawReply[1] << 24) | (rawReply[2] << 16) | (rawReply[3] << 8)) >> 8; diff --git a/linux/acs/CMakeLists.txt b/linux/acs/CMakeLists.txt index 99cacbc2..5b5b5ac8 100644 --- a/linux/acs/CMakeLists.txt +++ b/linux/acs/CMakeLists.txt @@ -1,6 +1,10 @@ -target_sources( - ${OBSW_NAME} PUBLIC AcsBoardPolling.cpp ImtqPollingTask.cpp RwPollingTask.cpp - SusPolling.cpp StrComHandler.cpp) +target_sources(${OBSW_NAME} PUBLIC AcsBoardPolling.cpp ImtqPollingTask.cpp + RwPollingTask.cpp SusPolling.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) target_sources(${OBSW_NAME} PRIVATE GpsHyperionLinuxController.cpp) diff --git a/linux/acs/StrComHandler.cpp b/linux/acs/StrComHandler.cpp index 303c2228..51c66f18 100644 --- a/linux/acs/StrComHandler.cpp +++ b/linux/acs/StrComHandler.cpp @@ -89,7 +89,7 @@ ReturnValue_t StrComHandler::performOperation(uint8_t operationCode) { break; } case InternalState::FIRMWARE_UPDATE: { - replyTimeout.setTimeout(200); + replyTimeout.setTimeout(2000); resetReplyHandlingState(); result = performFirmwareUpdate(); if (result == returnvalue::OK) { @@ -125,6 +125,7 @@ ReturnValue_t StrComHandler::startImageUpload(std::string fullname) { } { MutexGuard mg(lock); + replyWasReceived = false; state = InternalState::UPLOAD_IMAGE; } semaphore.release(); @@ -151,6 +152,7 @@ ReturnValue_t StrComHandler::startImageDownload(std::string path) { downloadImage.path = path; { MutexGuard mg(lock); + replyWasReceived = false; state = InternalState::DOWNLOAD_IMAGE; } terminate = false; @@ -187,6 +189,7 @@ ReturnValue_t StrComHandler::startFirmwareUpdate(std::string fullname) { flashWrite.lastRegion = static_cast(startracker::FirmwareRegions::LAST); { MutexGuard mg(lock); + replyWasReceived = false; state = InternalState::FIRMWARE_UPDATE; } semaphore.release(); @@ -216,6 +219,7 @@ ReturnValue_t StrComHandler::startFlashRead(std::string path, uint8_t startRegio flashRead.size = length; { MutexGuard mg(lock); + replyWasReceived = false; state = InternalState::FLASH_READ; } semaphore.release(); @@ -390,7 +394,8 @@ ReturnValue_t StrComHandler::performFlashWrite() { #endif ReturnValue_t result = returnvalue::OK; uint32_t size = 0; - uint32_t bytesWritten = 0; + uint32_t bytesWrittenInRegion = 0; + size_t totalBytesWritten = 0; uint32_t fileSize = 0; struct WriteActionRequest req; @@ -412,20 +417,18 @@ ReturnValue_t StrComHandler::performFlashWrite() { ProgressPrinter progressPrinter("Flash write", fileSize); #endif /* OBSW_DEBUG_STARTRACKER == 1 */ uint32_t fileChunks = fileSize / CHUNK_SIZE; - bytesWritten = 0; + bytesWrittenInRegion = 0; req.region = flashWrite.firstRegion; req.length = CHUNK_SIZE; - for (uint32_t idx = 0; idx < fileChunks; idx++) { - if (terminate) { - return returnvalue::OK; - } - file.seekg(idx * CHUNK_SIZE, file.beg); + + auto writeNextSegment = [&](uint32_t chunkIdx) { + file.seekg(chunkIdx * CHUNK_SIZE, file.beg); file.read(reinterpret_cast(req.data), CHUNK_SIZE); - if (bytesWritten + CHUNK_SIZE > FLASH_REGION_SIZE) { + if (bytesWrittenInRegion + CHUNK_SIZE > FLASH_REGION_SIZE) { req.region++; - bytesWritten = 0; + bytesWrittenInRegion = 0; } - req.address = bytesWritten; + req.address = bytesWrittenInRegion; arc_pack_write_action_req(&req, cmdBuf.data(), &size); result = sendAndRead(size, req.address); if (result != returnvalue::OK) { @@ -435,34 +438,49 @@ ReturnValue_t StrComHandler::performFlashWrite() { if (result != returnvalue::OK) { return result; } - bytesWritten += CHUNK_SIZE; + totalBytesWritten += CHUNK_SIZE; + bytesWrittenInRegion += CHUNK_SIZE; #if OBSW_DEBUG_STARTRACKER == 1 - progressPrinter.print(idx * CHUNK_SIZE); + progressPrinter.print(chunkIdx * CHUNK_SIZE); #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) { // Some grace time for other tasks TaskFactory::delayTask(2); } } uint32_t remainingBytes = fileSize - fileChunks * CHUNK_SIZE; - file.seekg((fileChunks - 1) * CHUNK_SIZE, file.beg); - file.read(reinterpret_cast(req.data), remainingBytes); - file.close(); - if (bytesWritten + CHUNK_SIZE > FLASH_REGION_SIZE) { - req.region++; - bytesWritten = 0; - } - req.address = bytesWritten; - req.length = remainingBytes; - bytesWritten += remainingBytes; - arc_pack_write_action_req(&req, cmdBuf.data(), &size); - result = sendAndRead(size, req.address); - if (result != returnvalue::OK) { - return result; - } - result = checkActionReply(replyLen); - if (result != returnvalue::OK) { - return result; + if (remainingBytes > 0) { + file.seekg(fileChunks * CHUNK_SIZE, file.beg); + file.read(reinterpret_cast(req.data), remainingBytes); + file.close(); + if (bytesWrittenInRegion + CHUNK_SIZE > FLASH_REGION_SIZE) { + req.region++; + bytesWrittenInRegion = 0; + } + req.address = bytesWrittenInRegion; + req.length = remainingBytes; + totalBytesWritten += CHUNK_SIZE; + bytesWrittenInRegion += remainingBytes; + arc_pack_write_action_req(&req, cmdBuf.data(), &size); + result = sendAndRead(size, req.address); + if (result != returnvalue::OK) { + return result; + } + result = checkActionReply(replyLen); + if (result != returnvalue::OK) { + return result; + } } #if OBSW_DEBUG_STARTRACKER == 1 progressPrinter.print(fileSize); diff --git a/linux/acs/StrComHandler.h b/linux/acs/StrComHandler.h index 385e8b2e..d8542f26 100644 --- a/linux/acs/StrComHandler.h +++ b/linux/acs/StrComHandler.h @@ -11,8 +11,6 @@ #include "bsp_q7s/fs/SdCardManager.h" #endif -#include "arcsec/client/generated/actionreq.h" -#include "arcsec/common/generated/tmtcstructs.h" #include "fsfw/devicehandlers/CookieIF.h" #include "fsfw/objectmanager/SystemObject.h" #include "fsfw/osal/linux/BinarySemaphore.h" @@ -20,6 +18,10 @@ #include "fsfw/tasks/ExecutableObjectIF.h" #include "fsfw_hal/linux/serial/SerialComIF.h" +extern "C" { +#include +} + /** * @brief Helper class for the star tracker handler to accelerate large data transfers. * diff --git a/linux/acs/SusPolling.cpp b/linux/acs/SusPolling.cpp index 0e09b02a..1da5ef5e 100644 --- a/linux/acs/SusPolling.cpp +++ b/linux/acs/SusPolling.cpp @@ -96,7 +96,7 @@ ReturnValue_t SusPolling::readReceivedMessage(CookieIF* cookie, uint8_t** buffer if (susIdx < 0) { return FAILED; } - if(susDevs[susIdx].replyResult != returnvalue::OK) { + if (susDevs[susIdx].replyResult != returnvalue::OK) { return susDevs[susIdx].replyResult; } MutexGuard mg(ipcLock); @@ -170,7 +170,7 @@ ReturnValue_t SusPolling::handleSusPolling() { susDevs[idx].ownReply.tempRaw = ((rawReply[0] & 0x0f) << 8) | rawReply[1]; // Reply is all ones. Sensor is probably off or faulty when // it should not be. - if(susDevs[idx].ownReply.tempRaw == 0x0fff) { + if (susDevs[idx].ownReply.tempRaw == 0x0fff) { susDevs[idx].replyResult = returnvalue::FAILED; } else { susDevs[idx].replyResult = returnvalue::OK; diff --git a/mission/acs/GyrAdis1650XHandler.cpp b/mission/acs/GyrAdis1650XHandler.cpp index 57033e80..b9e0b049 100644 --- a/mission/acs/GyrAdis1650XHandler.cpp +++ b/mission/acs/GyrAdis1650XHandler.cpp @@ -56,6 +56,9 @@ ReturnValue_t GyrAdis1650XHandler::buildNormalDeviceCommand(DeviceCommandId_t *i ReturnValue_t GyrAdis1650XHandler::buildTransitionDeviceCommand(DeviceCommandId_t *id) { switch (internalState) { case (InternalState::STARTUP): { + if (breakCountdown.isBusy()) { + return NOTHING_TO_SEND; + } *id = adis1650x::REQUEST; return preparePeriodicRequest(acs::SimpleSensorMode::NORMAL); } diff --git a/mission/acs/str/ArcsecDatalinkLayer.cpp b/mission/acs/str/ArcsecDatalinkLayer.cpp index 532c12b8..d67f6e75 100644 --- a/mission/acs/str/ArcsecDatalinkLayer.cpp +++ b/mission/acs/str/ArcsecDatalinkLayer.cpp @@ -1,4 +1,8 @@ -#include +#include "ArcsecDatalinkLayer.h" + +extern "C" { +#include +} ArcsecDatalinkLayer::ArcsecDatalinkLayer() : decodeRingBuf(BUFFER_LENGTHS, true) { slipInit(); } @@ -18,13 +22,10 @@ ReturnValue_t ArcsecDatalinkLayer::checkRingBufForFrame(const uint8_t** decodedF case ARC_DEC_INPROGRESS: { break; } - case ARC_DEC_ERROR_FRAME_SHORT: { + case ARC_DEC_ERROR: { 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_SYNC: { // Reset length of SLIP struct for next frame diff --git a/mission/acs/str/ArcsecDatalinkLayer.h b/mission/acs/str/ArcsecDatalinkLayer.h index 404d9e52..1453e517 100644 --- a/mission/acs/str/ArcsecDatalinkLayer.h +++ b/mission/acs/str/ArcsecDatalinkLayer.h @@ -5,10 +5,13 @@ #include #include -#include "arcsec/common/misc.h" #include "eive/resultClassIds.h" #include "fsfw/returnvalues/returnvalue.h" +extern "C" { +#include +} + /** * @brief Helper class to handle the datalinklayer of replies from the star tracker of arcsec. */ diff --git a/mission/acs/str/ArcsecJsonParamBase.cpp b/mission/acs/str/ArcsecJsonParamBase.cpp index b5dd2150..58c54d30 100644 --- a/mission/acs/str/ArcsecJsonParamBase.cpp +++ b/mission/acs/str/ArcsecJsonParamBase.cpp @@ -3,6 +3,10 @@ #include "arcsecJsonKeys.h" +extern "C" { +#include +} + ArcsecJsonParamBase::ArcsecJsonParamBase(std::string setName) : setName(setName) {} ReturnValue_t ArcsecJsonParamBase::create(uint8_t* buffer) { diff --git a/mission/acs/str/ArcsecJsonParamBase.h b/mission/acs/str/ArcsecJsonParamBase.h index 90c54f45..4c4a7a90 100644 --- a/mission/acs/str/ArcsecJsonParamBase.h +++ b/mission/acs/str/ArcsecJsonParamBase.h @@ -7,8 +7,6 @@ #include #include -#include "arcsec/common/generated/tmtcstructs.h" -#include "arcsec/common/genericstructs.h" #include "eive/resultClassIds.h" #include "fsfw/returnvalues/returnvalue.h" diff --git a/mission/acs/str/StarTrackerHandler.cpp b/mission/acs/str/StarTrackerHandler.cpp index 3837d9e5..54af25da 100644 --- a/mission/acs/str/StarTrackerHandler.cpp +++ b/mission/acs/str/StarTrackerHandler.cpp @@ -1,18 +1,18 @@ -#include -#include -#include #include #include #include #include #include +extern "C" { +#include +} + #include #include #include #include "OBSWConfig.h" -#include "arcsec/common/misc.h" std::atomic_bool JCFG_DONE(false); diff --git a/mission/acs/str/StarTrackerHandler.h b/mission/acs/str/StarTrackerHandler.h index f143a660..ea46cb43 100644 --- a/mission/acs/str/StarTrackerHandler.h +++ b/mission/acs/str/StarTrackerHandler.h @@ -10,12 +10,15 @@ #include #include "OBSWConfig.h" -#include "arcsec/common/SLIP.h" #include "devices/powerSwitcherList.h" #include "fsfw/devicehandlers/DeviceHandlerBase.h" #include "fsfw/src/fsfw/serialize/SerializeAdapter.h" #include "fsfw/timemanager/Countdown.h" +extern "C" { +#include +} + /** * @brief This is the device handler for the star tracker from arcsec. * diff --git a/mission/cfdp/CMakeLists.txt b/mission/cfdp/CMakeLists.txt index 8b137891..71d62ce8 100644 --- a/mission/cfdp/CMakeLists.txt +++ b/mission/cfdp/CMakeLists.txt @@ -1 +1 @@ - +target_sources(${LIB_EIVE_MISSION} PRIVATE CfdpHandler.cpp) diff --git a/mission/cfdp/CfdpHandler.cpp b/mission/cfdp/CfdpHandler.cpp new file mode 100644 index 00000000..902097b6 --- /dev/null +++ b/mission/cfdp/CfdpHandler.cpp @@ -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(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; +} diff --git a/mission/cfdp/CfdpHandler.h b/mission/cfdp/CfdpHandler.h new file mode 100644 index 00000000..2de9f7dd --- /dev/null +++ b/mission/cfdp/CfdpHandler.h @@ -0,0 +1,71 @@ +#ifndef FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H +#define FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H + +#include + +#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 diff --git a/mission/controller/ThermalController.cpp b/mission/controller/ThermalController.cpp index 96b6de86..82efb85c 100644 --- a/mission/controller/ThermalController.cpp +++ b/mission/controller/ThermalController.cpp @@ -21,7 +21,8 @@ #define LOWER_EBAND_UPPER_LIMITS 0 #define LOWER_PLOC_UPPER_LIMITS 0 -ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater) +ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater, + const std::atomic_bool& tcsBoardShortUnavailable) : ExtendedControllerBase(objectId), heaterHandler(heater), sensorTemperatures(this), @@ -66,7 +67,8 @@ ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater susSet8(objects::SUS_8_R_LOC_XBYBZB_PT_YB), susSet9(objects::SUS_9_R_LOC_XBYBZB_PT_YF), susSet10(objects::SUS_10_N_LOC_XMYBZF_PT_ZF), - susSet11(objects::SUS_11_R_LOC_XBYMZB_PT_ZB) { + susSet11(objects::SUS_11_R_LOC_XBYMZB_PT_ZB), + tcsBrdShortlyUnavailable(tcsBoardShortUnavailable) { resetSensorsArray(); } @@ -134,10 +136,12 @@ void ThermalController::performControlOperation() { } } - { - PoolReadGuard pg(&sensorTemperatures); - if (pg.getReadResult() == returnvalue::OK) { - copySensors(); + if (not tcsBrdShortlyUnavailable) { + { + PoolReadGuard pg(&sensorTemperatures); + if (pg.getReadResult() == returnvalue::OK) { + copySensors(); + } } } { @@ -166,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); } - } 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); } - cycles++; } 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, 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; } @@ -972,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; @@ -1019,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); + } } } } @@ -1264,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; @@ -1553,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); } } } @@ -1589,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) { @@ -1607,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; } @@ -1630,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"); } } } @@ -1698,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) { @@ -1723,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); } @@ -1742,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) { @@ -1751,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 631a37aa..7aa05b1e 100644 --- a/mission/controller/ThermalController.h +++ b/mission/controller/ThermalController.h @@ -22,6 +22,7 @@ #include #include +#include #include /** @@ -47,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 { @@ -89,25 +95,28 @@ 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; 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; 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; }; @@ -181,6 +190,10 @@ class ThermalController : public ExtendedControllerBase { susMax1227::SusDataset susSet10; susMax1227::SusDataset susSet11; + // If the TCS board in unavailable, for example due to a recovery, skip + // some TCS controller tasks to avoid unnecessary events. + const std::atomic_bool& tcsBrdShortlyUnavailable = false; + lp_var_t tempQ7s = lp_var_t(objects::CORE_CONTROLLER, core::PoolIds::TEMPERATURE); lp_var_t battTemp1 = lp_var_t(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_1); lp_var_t battTemp2 = lp_var_t(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_2); @@ -257,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. @@ -286,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(); @@ -296,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(); @@ -323,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/genericFactory.cpp b/mission/genericFactory.cpp index ce338097..9889fe9e 100644 --- a/mission/genericFactory.cpp +++ b/mission/genericFactory.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -23,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +48,7 @@ #include "mission/system/acs/RwAssembly.h" #include "mission/system/acs/acsModeTree.h" #include "mission/system/tree/tcsModeTree.h" +#include "mission/tcs/defs.h" #include "mission/tmtc/tmFilters.h" #include "objects/systemObjectList.h" #include "tmtc/pusIds.h" @@ -90,6 +91,8 @@ EiveFaultHandler EIVE_FAULT_HANDLER; } // namespace cfdp +std::atomic_bool tcs::TCS_BOARD_SHORTLY_UNAVAILABLE = false; + void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFunnel** pusFunnel, CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan, StorageManagerIF** ipcStore, StorageManagerIF** tmStore, @@ -300,7 +303,8 @@ void ObjectFactory::createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF& } void ObjectFactory::createThermalController(HeaterHandler& heaterHandler) { - auto* tcsCtrl = new ThermalController(objects::THERMAL_CONTROLLER, heaterHandler); + auto* tcsCtrl = new ThermalController(objects::THERMAL_CONTROLLER, heaterHandler, + tcs::TCS_BOARD_SHORTLY_UNAVAILABLE); tcsCtrl->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); } void ObjectFactory::createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch, @@ -366,10 +370,12 @@ void ObjectFactory::createAcsBoardAssy(PowerSwitchIF& pwrSwitcher, acsAss->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM); } -TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher) { +TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher, + std::atomic_bool& tcsShortlyUnavailable) { TcsBoardHelper helper(RTD_INFOS); - auto* tcsBoardAss = new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher, - power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper); + auto* tcsBoardAss = + new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher, + power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper, tcsShortlyUnavailable); tcsBoardAss->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); return tcsBoardAss; } diff --git a/mission/genericFactory.h b/mission/genericFactory.h index 496c516b..6cd2068d 100644 --- a/mission/genericFactory.h +++ b/mission/genericFactory.h @@ -55,7 +55,8 @@ void createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch, void createSusAssy(PowerSwitchIF& pwrSwitcher, std::array suses); void createAcsBoardAssy(PowerSwitchIF& pwrSwitcher, std::array assemblyDhbs, ExtendedControllerBase* gpsCtrl, GpioIF* gpioComIF); -TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher); +TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher, + std::atomic_bool& tcsShortlyUnavailable); } // namespace ObjectFactory diff --git a/mission/power/defs.h b/mission/power/defs.h index b10fe1ff..13e5e63f 100644 --- a/mission/power/defs.h +++ b/mission/power/defs.h @@ -50,6 +50,7 @@ static constexpr Event FDIR_REACTION_IGNORED = event::makeEvent(SUBSYSTEM_ID, 3, enum class States { IDLE, SWITCHING_POWER, CHECKING_POWER, MODE_COMMANDING }; enum class OpCodes { NONE, TO_OFF_DONE, TO_NOT_OFF_DONE, TIMEOUT_OCCURED }; +enum RecoveryCustomStates { IDLE, POWER_SWITCHING_OFF, POWER_SWITCHING_ON, DONE }; } // namespace power namespace duallane { diff --git a/mission/sysDefs.h b/mission/sysDefs.h index 7a729d6e..e8ada9f8 100644 --- a/mission/sysDefs.h +++ b/mission/sysDefs.h @@ -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 { + +// 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 ANNOUNCE_VERSION = 1; 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 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; diff --git a/mission/system/CMakeLists.txt b/mission/system/CMakeLists.txt index 51e47ef4..e5473de8 100644 --- a/mission/system/CMakeLists.txt +++ b/mission/system/CMakeLists.txt @@ -6,5 +6,6 @@ add_subdirectory(fdir) add_subdirectory(power) target_sources( - ${LIB_EIVE_MISSION} PRIVATE systemTree.cpp DualLanePowerStateMachine.cpp - EiveSystem.cpp treeUtil.cpp) + ${LIB_EIVE_MISSION} + PRIVATE systemTree.cpp DualLanePowerStateMachine.cpp EiveSystem.cpp + treeUtil.cpp SharedPowerAssemblyBase.cpp) diff --git a/mission/system/SharedPowerAssemblyBase.cpp b/mission/system/SharedPowerAssemblyBase.cpp new file mode 100644 index 00000000..5c325b51 --- /dev/null +++ b/mission/system/SharedPowerAssemblyBase.cpp @@ -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; +} diff --git a/mission/system/SharedPowerAssemblyBase.h b/mission/system/SharedPowerAssemblyBase.h new file mode 100644 index 00000000..c269a52f --- /dev/null +++ b/mission/system/SharedPowerAssemblyBase.h @@ -0,0 +1,27 @@ +#ifndef MISSION_SYSTEM_SHAREDPOWERASSEMBLYBASE_H_ +#define MISSION_SYSTEM_SHAREDPOWERASSEMBLYBASE_H_ + +#include +#include +#include + +/** + * 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_ */ diff --git a/mission/system/acs/DualLaneAssemblyBase.h b/mission/system/acs/DualLaneAssemblyBase.h index 40d9aefc..a30bad5b 100644 --- a/mission/system/acs/DualLaneAssemblyBase.h +++ b/mission/system/acs/DualLaneAssemblyBase.h @@ -39,12 +39,7 @@ class DualLaneAssemblyBase : public AssemblyBase, public ConfirmsFailuresIF { SideSwitchState sideSwitchState = SideSwitchState::NONE; duallane::Submodes targetSubmodeForSideSwitch = duallane::Submodes::B_SIDE; - enum RecoveryCustomStates { - IDLE, - POWER_SWITCHING_OFF, - POWER_SWITCHING_ON, - DONE - } customRecoveryStates = RecoveryCustomStates::IDLE; + power::RecoveryCustomStates customRecoveryStates = power::RecoveryCustomStates::IDLE; MessageQueueIF* eventQueue = nullptr; diff --git a/mission/system/acs/RwAssembly.cpp b/mission/system/acs/RwAssembly.cpp index eacd8d0d..68b7c2da 100644 --- a/mission/system/acs/RwAssembly.cpp +++ b/mission/system/acs/RwAssembly.cpp @@ -1,8 +1,8 @@ #include "RwAssembly.h" -RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher, +RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switchId, RwHelper helper) - : AssemblyBase(objectId), helper(helper), switcher(pwrSwitcher, switcher) { + : SharedPowerAssemblyBase(objectId, pwrSwitcher, switchId), helper(helper) { ModeListEntry entry; for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) { entry.setObject(helper.rwIds[idx]); @@ -12,26 +12,8 @@ RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power:: } } -void RwAssembly::performChildOperation() { - auto state = switcher.getState(); - if (state != PowerSwitcher::WAIT_OFF and state != PowerSwitcher::WAIT_ON) { - AssemblyBase::performChildOperation(); - return; - } - switcher.doStateMachine(); - if (state == PowerSwitcher::WAIT_OFF and switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) { - // Indicator that a transition to off is finished - AssemblyBase::handleModeReached(); - } else if (state == PowerSwitcher::WAIT_ON and - switcher.getState() == PowerSwitcher::SWITCH_IS_ON) { - // Indicator that mode commanding can be performed now - AssemblyBase::startTransition(targetMode, targetSubmode); - } -} - ReturnValue_t RwAssembly::commandChildren(Mode_t mode, Submode_t submode) { ReturnValue_t result = returnvalue::OK; - modeTransitionFailedSwitch = true; // Initialize the mode table to ensure all devices are in a defined state for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) { modeTable[idx].setMode(MODE_OFF); @@ -76,36 +58,6 @@ ReturnValue_t RwAssembly::isModeCombinationValid(Mode_t mode, Submode_t submode) return HasModesIF::INVALID_MODE; } -void RwAssembly::startTransition(Mode_t mode, Submode_t submode) { - if (mode != MODE_OFF) { - switcher.turnOn(true); - switcher.doStateMachine(); - if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) { - AssemblyBase::startTransition(mode, submode); - } else { - // Need to wait with mode commanding until power switcher is done - targetMode = mode; - targetSubmode = submode; - } - } else { - // Perform regular mode commanding first - AssemblyBase::startTransition(mode, submode); - } -} - -void RwAssembly::handleModeReached() { - if (targetMode == MODE_OFF) { - switcher.turnOff(true); - switcher.doStateMachine(); - // Need to wait with call to AssemblyBase::handleModeReached until power switcher is done - if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) { - AssemblyBase::handleModeReached(); - } - } else { - AssemblyBase::handleModeReached(); - } -} - ReturnValue_t RwAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode) { ReturnValue_t result = returnvalue::OK; object_id_t objId = 0; diff --git a/mission/system/acs/RwAssembly.h b/mission/system/acs/RwAssembly.h index 540e37cc..178595b4 100644 --- a/mission/system/acs/RwAssembly.h +++ b/mission/system/acs/RwAssembly.h @@ -1,8 +1,8 @@ #ifndef MISSION_SYSTEM_RWASS_H_ #define MISSION_SYSTEM_RWASS_H_ -#include #include +#include struct RwHelper { RwHelper(std::array rwIds) : rwIds(rwIds) {} @@ -10,17 +10,15 @@ struct RwHelper { std::array rwIds = {}; }; -class RwAssembly : public AssemblyBase { +class RwAssembly : public SharedPowerAssemblyBase { public: - RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher, + RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switchId, RwHelper helper); private: static constexpr uint8_t NUMBER_RWS = 4; RwHelper helper; - PowerSwitcher switcher; bool warningSwitch = true; - bool modeTransitionFailedSwitch = true; FixedArrayList modeTable; ReturnValue_t initialize() override; @@ -35,12 +33,9 @@ class RwAssembly : public AssemblyBase { bool isUseable(object_id_t object, Mode_t mode); // AssemblyBase implementation - void performChildOperation() override; ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override; ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override; ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override; - void startTransition(Mode_t mode, Submode_t submode) override; - void handleModeReached() override; }; #endif /* MISSION_SYSTEM_RWASS_H_ */ diff --git a/mission/system/fdir/RtdFdir.cpp b/mission/system/fdir/RtdFdir.cpp index 008dc405..24ebc2a2 100644 --- a/mission/system/fdir/RtdFdir.cpp +++ b/mission/system/fdir/RtdFdir.cpp @@ -3,4 +3,4 @@ #include "eive/objects.h" RtdFdir::RtdFdir(object_id_t sensorId) - : DeviceHandlerFailureIsolation(sensorId, objects::TCS_BOARD_ASS) {} + : DeviceHandlerFailureIsolation(sensorId, objects::NO_OBJECT) {} diff --git a/mission/system/objects/TcsBoardAssembly.cpp b/mission/system/objects/TcsBoardAssembly.cpp index b32a00a9..2a5a3e7b 100644 --- a/mission/system/objects/TcsBoardAssembly.cpp +++ b/mission/system/objects/TcsBoardAssembly.cpp @@ -4,9 +4,11 @@ #include TcsBoardAssembly::TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, - power::Switch_t theSwitch, TcsBoardHelper helper) - : AssemblyBase(objectId, 24), switcher(pwrSwitcher, theSwitch), helper(helper) { - eventQueue = QueueFactory::instance()->createMessageQueue(24); + power::Switch_t theSwitch, TcsBoardHelper helper, + std::atomic_bool& tcsShortlyUnavailable) + : SharedPowerAssemblyBase(objectId, pwrSwitcher, theSwitch, 16), + helper(helper), + tcsShortlyUnavailable(tcsShortlyUnavailable) { ModeListEntry entry; for (uint8_t idx = 0; idx < NUMBER_RTDS; idx++) { entry.setObject(helper.rtdInfos[idx].first); @@ -16,23 +18,6 @@ TcsBoardAssembly::TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitc } } -void TcsBoardAssembly::performChildOperation() { - auto state = switcher.getState(); - if (state != PowerSwitcher::WAIT_OFF and state != PowerSwitcher::WAIT_ON) { - AssemblyBase::performChildOperation(); - return; - } - switcher.doStateMachine(); - if (state == PowerSwitcher::WAIT_OFF and switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) { - // Indicator that a transition to off is finished - AssemblyBase::handleModeReached(); - } else if (state == PowerSwitcher::WAIT_ON and - switcher.getState() == PowerSwitcher::SWITCH_IS_ON) { - // Indicator that mode commanding can be performed now - AssemblyBase::startTransition(targetMode, targetSubmode); - } -} - ReturnValue_t TcsBoardAssembly::commandChildren(Mode_t mode, Submode_t submode) { ReturnValue_t result = returnvalue::OK; // Initialize the mode table to ensure all devices are in a defined state @@ -50,6 +35,8 @@ ReturnValue_t TcsBoardAssembly::commandChildren(Mode_t mode, Submode_t submode) if (mode == DeviceHandlerIF::MODE_NORMAL or mode == MODE_ON) { result = handleNormalOrOnModeCmd(mode, submode); } + } else { + tcsShortlyUnavailable = true; } HybridIterator tableIter(modeTable.begin(), modeTable.end()); executeTable(tableIter); @@ -94,25 +81,6 @@ ReturnValue_t TcsBoardAssembly::isModeCombinationValid(Mode_t mode, Submode_t su return HasModesIF::INVALID_MODE; } -ReturnValue_t TcsBoardAssembly::initialize() { return AssemblyBase::initialize(); } - -void TcsBoardAssembly::startTransition(Mode_t mode, Submode_t submode) { - if (mode != MODE_OFF) { - switcher.turnOn(true); - switcher.doStateMachine(); - if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) { - AssemblyBase::startTransition(mode, submode); - } else { - // Need to wait with mode commanding until power switcher is done - targetMode = mode; - targetSubmode = submode; - } - } else { - // Perform regular mode commanding first - AssemblyBase::startTransition(mode, submode); - } -} - ReturnValue_t TcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode) { ReturnValue_t result = returnvalue::OK; bool needsSecondStep = false; @@ -169,21 +137,6 @@ bool TcsBoardAssembly::isUseable(object_id_t object, Mode_t mode) { return false; } -MessageQueueId_t TcsBoardAssembly::getEventReceptionQueue() { return eventQueue->getId(); } - -void TcsBoardAssembly::handleModeReached() { - if (targetMode == MODE_OFF) { - switcher.turnOff(true); - switcher.doStateMachine(); - // Need to wait with call to AssemblyBase::handleModeReached until power switcher is done - if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) { - AssemblyBase::handleModeReached(); - } - } else { - AssemblyBase::handleModeReached(); - } -} - void TcsBoardAssembly::handleChildrenLostMode(ReturnValue_t result) { triggerEvent(CHILDREN_LOST_MODE, result); startTransition(mode, submode); @@ -210,6 +163,12 @@ ReturnValue_t TcsBoardAssembly::checkAndHandleHealthStates(Mode_t deviceMode, return status; } +bool TcsBoardAssembly::checkAndHandleRecovery() { + bool recoveryPending = SharedPowerAssemblyBase::checkAndHandleRecovery(); + tcsShortlyUnavailable = recoveryPending; + return recoveryPending; +} + void TcsBoardAssembly::handleModeTransitionFailed(ReturnValue_t result) { if (targetMode == MODE_OFF) { AssemblyBase::handleModeTransitionFailed(result); diff --git a/mission/system/objects/TcsBoardAssembly.h b/mission/system/objects/TcsBoardAssembly.h index 3c3fc0d6..da10999c 100644 --- a/mission/system/objects/TcsBoardAssembly.h +++ b/mission/system/objects/TcsBoardAssembly.h @@ -4,6 +4,10 @@ #include #include #include +#include +#include + +#include #include "events/subsystemIdRanges.h" #include "returnvalues/classIds.h" @@ -15,23 +19,20 @@ struct TcsBoardHelper { std::array, 16> rtdInfos = {}; }; -class TcsBoardAssembly : public AssemblyBase, public ConfirmsFailuresIF { +class TcsBoardAssembly : public SharedPowerAssemblyBase { public: static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::TCS_BOARD_ASS; static constexpr Event CHILDREN_LOST_MODE = event::makeEvent(SUBSYSTEM_ID, 0, severity::MEDIUM); TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher, - TcsBoardHelper helper); - - ReturnValue_t initialize() override; + TcsBoardHelper helper, std::atomic_bool& tcsShortlyUnavailable); private: static constexpr uint8_t NUMBER_RTDS = 16; - PowerSwitcher switcher; bool warningSwitch = true; TcsBoardHelper helper; FixedArrayList modeTable; - MessageQueueIF* eventQueue = nullptr; + std::atomic_bool& tcsShortlyUnavailable; ReturnValue_t handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode); /** @@ -42,17 +43,12 @@ class TcsBoardAssembly : public AssemblyBase, public ConfirmsFailuresIF { */ bool isUseable(object_id_t object, Mode_t mode); - // ConfirmFailureIF implementation - MessageQueueId_t getEventReceptionQueue() override; - // AssemblyBase implementation - void performChildOperation() override; ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override; ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override; ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override; - void startTransition(Mode_t mode, Submode_t submode) override; - void handleModeReached() override; ReturnValue_t checkAndHandleHealthStates(Mode_t deviceMode, Submode_t deviceSubmode); + bool checkAndHandleRecovery() override; // These two overrides prevent a transition of the whole assembly back to off just because // some devices are not working 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/Max31865EiveHandler.cpp b/mission/tcs/Max31865EiveHandler.cpp index 284a2d0c..599071ef 100644 --- a/mission/tcs/Max31865EiveHandler.cpp +++ b/mission/tcs/Max31865EiveHandler.cpp @@ -1,6 +1,8 @@ #include #include +#include "fsfw/thermal/tcsDefinitions.h" + Max31865EiveHandler::Max31865EiveHandler(object_id_t objectId, object_id_t comIF, CookieIF* comCookie) : DeviceHandlerBase(objectId, comIF, comCookie, nullptr), @@ -152,6 +154,15 @@ ReturnValue_t Max31865EiveHandler::interpretDeviceReply(DeviceCommandId_t id, transitionOk = true; 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 float rtdValue = exchangeStruct.adcCode * EiveMax31855::RTD_RREF_PT1000 / INT16_MAX; diff --git a/common/config/devices/heaterSwitcherList.h b/mission/tcs/defs.h similarity index 55% rename from common/config/devices/heaterSwitcherList.h rename to mission/tcs/defs.h index 8f91385d..cb1a73e4 100644 --- a/common/config/devices/heaterSwitcherList.h +++ b/mission/tcs/defs.h @@ -1,10 +1,10 @@ -#ifndef FSFWCONFIG_DEVICES_HEATERSWITCHERLIST_H_ -#define FSFWCONFIG_DEVICES_HEATERSWITCHERLIST_H_ +#pragma once +#include #include namespace heater { -enum Switchers : uint8_t { +enum Switch : uint8_t { HEATER_0_OBC_BRD, HEATER_1_PLOC_PROC_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; + +} diff --git a/scripts/install-obsw-yocto.sh b/scripts/install-obsw-yocto.sh index 08fb78d3..ae6fbee7 100755 --- a/scripts/install-obsw-yocto.sh +++ b/scripts/install-obsw-yocto.sh @@ -11,7 +11,7 @@ fi init_dir=$(pwd) obsw_root="" -q7s_yocto_dir="q7s-yocto" +q7s_yocto_dir="yocto" q7s_package_path="q7s-package/${q7s_yocto_dir}" obsw_version_filename="obsw_version.txt" diff --git a/thirdparty/CMakeLists.txt b/thirdparty/CMakeLists.txt index e300cd11..3a5110c9 100644 --- a/thirdparty/CMakeLists.txt +++ b/thirdparty/CMakeLists.txt @@ -4,7 +4,8 @@ endif() # Dependency on proprietary library if(TGT_BSP MATCHES "arm/q7s") - add_subdirectory(arcsec_star_tracker) + # Only add required folder for wire library. + add_subdirectory(sagittactl/wire) endif() add_subdirectory(rapidcsv) diff --git a/thirdparty/arcsec_star_tracker b/thirdparty/arcsec_star_tracker deleted file mode 160000 index 2823952e..00000000 --- a/thirdparty/arcsec_star_tracker +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2823952e0902726e6e35dd7c159761f76bf7e505 diff --git a/thirdparty/sagittactl b/thirdparty/sagittactl new file mode 160000 index 00000000..64332216 --- /dev/null +++ b/thirdparty/sagittactl @@ -0,0 +1 @@ +Subproject commit 64332216c193fa6483258060b53fc2842c08dcf4 diff --git a/tmtc b/tmtc index 43b530cd..f8da9cff 160000 --- a/tmtc +++ b/tmtc @@ -1 +1 @@ -Subproject commit 43b530cdb7dfe6774962bf4b8a880e1c9a6e6580 +Subproject commit f8da9cff7c5d6d6bdd483f90ccefb67b2d1e11a4 diff --git a/unittest/controller/testThermalController.cpp b/unittest/controller/testThermalController.cpp index 623c6d52..c2166ca0 100644 --- a/unittest/controller/testThermalController.cpp +++ b/unittest/controller/testThermalController.cpp @@ -15,6 +15,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") { const object_id_t THERMAL_CONTROLLER_ID = 0x123; + std::atomic_bool tcsBrdShortlyUnavailable = false; TemperatureSensorInserter::Max31865DummyMap map0; TemperatureSensorInserter::Tmp1075DummyMap map1; @@ -29,7 +30,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") { // testEnvironment::initialize(); - ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler); + ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler, tcsBrdShortlyUnavailable); ReturnValue_t result = controller.initialize(); REQUIRE(result == returnvalue::OK); @@ -46,8 +47,8 @@ TEST_CASE("Thermal Controller", "[ThermalController]") { CommandMessage modeMessage; - ModeMessage::setModeMessage(&modeMessage, ModeMessage::CMD_MODE_COMMAND, - ControllerBase::MODE_NORMAL, HasModesIF::SUBMODE_NONE); + ModeMessage::setModeMessage(&modeMessage, ModeMessage::CMD_MODE_COMMAND, HasModesIF::MODE_ON, + HasModesIF::SUBMODE_NONE); MessageQueueIF* commandQueue = QueueFactory::instance()->createMessageQueue(5, MessageQueueMessage::MAX_MESSAGE_SIZE); @@ -57,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);