diff --git a/.gitignore b/.gitignore index a5065673..5419ca42 100644 --- a/.gitignore +++ b/.gitignore @@ -12,8 +12,13 @@ #vscode /.vscode +# IntelliJ +/.idea/* + # Python __pycache__ -.idea + +# CLion +!/.idea/cmake.xml generators/*.db diff --git a/.run/Q7S FM.run.xml b/.run/Q7S FM.run.xml new file mode 100644 index 00000000..ea4ae083 --- /dev/null +++ b/.run/Q7S FM.run.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 51b7209d..b078c4d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,49 @@ will consitute of a breaking change warranting a new major release: ## Fixed +- Linux GPS handler now checks the individual `*_SET` flags when analysing the `gpsd` struct. + PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/400 + +# [v1.32.0] + +eive-tmtc: v2.16.1 + +## Fixed + - ADIS1650X: Added missing MDL_RANG pool entry for configuration set +- Bumped FSFW for bugfix in health service: No execution complete for targeted health announce + command. +- Removed matrix determinant calculation as part of the `MEKF`, which would take about + 300ms of runtime +- Resetting the `MEKF` now also actually resets its stored state +- Bumped FSFW for bugfix in destination handler: Better error handling and able to process + destination folder path. + +## Changed + +- Added basic persistent TM store for PUS telemetry and basic interface to dump and delete + telemetry. Implementation is based on a timed rotating files, with the addition that files + might be generated more often if the maximum file size of 8192 bytes is exceeded. + PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/320/files +- Commented out commanding of actuators as part of the `AcsController` +- Collection sets of the `AcsController` now get updated before running the actual ACS + algorithm +- `GpsController` now always gets scheduled +- The `CoreController` now initializes the initial clock from the time file as early as possible + (in the constructor) if possible, which should usually be the case. + +## Added + +- Added basic persistent TM store for PUS telemetry and basic interface to dump and delete + telemetry. + PR: https://egit.irs.uni-stuttgart.de/eive/eive-obsw/pulls/320/files +- `ExecutableComIfDummy` class to have a dummy for classes like the RTD polling class. +- Added `AcsController` action command to confirm solar array deployment, which then deletes + two files +- Added `AcsController` action command to reset `MEKF` +- `GpsCtrlDummy` now initializes the `gpsSet` +- `RwDummy` now initializes with a non faulty state + # [v1.31.1] diff --git a/CMakeLists.txt b/CMakeLists.txt index a0768ddd..a9eef222 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,8 +10,8 @@ cmake_minimum_required(VERSION 3.13) set(OBSW_VERSION_MAJOR 1) -set(OBSW_VERSION_MINOR 31) -set(OBSW_VERSION_REVISION 1) +set(OBSW_VERSION_MINOR 32) +set(OBSW_VERSION_REVISION 0) # set(CMAKE_VERBOSE TRUE) @@ -223,6 +223,7 @@ set(LIB_JSON_PATH ${THIRD_PARTY_FOLDER}/json) set(FSFW_WARNING_SHADOW_LOCAL_GCC OFF) set(EIVE_ADD_LINUX_FILES OFF) +set(FSFW_ADD_TMSTORAGE ON) # Analyse different OS and architecture/target options, determine BSP_PATH, # display information about compiler etc. diff --git a/README.md b/README.md index b6fa64e9..cd350386 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ 11. [Q7S OBC](#q7s) 12. [Static Code Analysis](#static-code-analysis) 13. [Eclipse](#eclipse) +14. [CLion](#clion) 14. [Running the OBSW on a Raspberry Pi](#rpi) 15. [Running OBSW on EGSE](#egse) 16. [Manually preparing sysroots to compile gpsd](#gpsd) @@ -1229,6 +1230,22 @@ Finally, you can convert the generated `.xml` file to HTML with the following co cppcheck-htmlreport --file=report.xml --report-dir=cppcheck --source-dir=.. ``` +# CLion + +CLion is the recommended IDE for the development of the hosted version of EIVE. +You can also set up CLion for cross-compilation of the primary OBSW. + +There is a shared `.idea/cmake.xml` file to get started with this. +To make cross-compilation work, two special environment variables +need to be set: + +- `ZYNQ_7020_ROOTFS` pointing to the root filesystem +- `CROSS_COMPILE` pointing to the the full path of the cross-compiler + without the specific tool suffix. For example, if the the cross-compiler + tools are located at `/opt/q7s-gcc/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin`, + this variable would be set + to `/opt/q7s-gcc/gcc-arm-8.3-2019.03-x86_64-arm-linux-gnueabihf/bin/arm-linux-gnueabihf` + # Eclipse When using Eclipse, there are two special build variables in the project properties diff --git a/bsp_hosted/ObjectFactory.cpp b/bsp_hosted/ObjectFactory.cpp index b0c4ad7f..5ef9e2c2 100644 --- a/bsp_hosted/ObjectFactory.cpp +++ b/bsp_hosted/ObjectFactory.cpp @@ -8,6 +8,7 @@ #include #include +#include "../mission/utility/DummySdCardManager.h" #include "OBSWConfig.h" #include "fsfw/platform.h" #include "fsfw_tests/integration/task/TestTask.h" @@ -57,7 +58,8 @@ void ObjectFactory::produce(void* args) { Factory::setStaticFrameworkObjectIds(); PusTmFunnel* pusFunnel; CfdpTmFunnel* cfdpFunnel; - ObjectFactory::produceGenericObjects(nullptr, &pusFunnel, &cfdpFunnel); + auto sdcMan = new DummySdCardManager("/tmp"); + ObjectFactory::produceGenericObjects(nullptr, &pusFunnel, &cfdpFunnel, *sdcMan); auto* dummyGpioIF = new DummyGpioIF(); auto* dummySwitcher = new DummyPowerSwitcher(objects::PCDU_HANDLER, 18, 0); diff --git a/bsp_hosted/fsfwconfig/events/translateEvents.cpp b/bsp_hosted/fsfwconfig/events/translateEvents.cpp index e35a31ef..c979ff5e 100644 --- a/bsp_hosted/fsfwconfig/events/translateEvents.cpp +++ b/bsp_hosted/fsfwconfig/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** - * @brief Auto-generated event translation file. Contains 260 translations. + * @brief Auto-generated event translation file. Contains 263 translations. * @details - * Generated on: 2023-02-23 15:39:20 + * Generated on: 2023-02-24 16:57:00 */ #include "translateEvents.h" @@ -90,6 +90,8 @@ const char *CHANGE_OF_SETUP_PARAMETER_STRING = "CHANGE_OF_SETUP_PARAMETER"; const char *STORE_ERROR_STRING = "STORE_ERROR"; const char *MSG_QUEUE_ERROR_STRING = "MSG_QUEUE_ERROR"; const char *SERIALIZATION_ERROR_STRING = "SERIALIZATION_ERROR"; +const char *FILESTORE_ERROR_STRING = "FILESTORE_ERROR"; +const char *FILENAME_TOO_LARGE_ERROR_STRING = "FILENAME_TOO_LARGE_ERROR"; const char *SAFE_RATE_VIOLATION_STRING = "SAFE_RATE_VIOLATION"; const char *SAFE_RATE_RECOVERY_STRING = "SAFE_RATE_RECOVERY"; const char *MULTIPLE_RW_INVALID_STRING = "MULTIPLE_RW_INVALID"; @@ -253,6 +255,7 @@ const char *REBOOT_HW_STRING = "REBOOT_HW"; const char *NO_SD_CARD_ACTIVE_STRING = "NO_SD_CARD_ACTIVE"; const char *VERSION_INFO_STRING = "VERSION_INFO"; const char *CURRENT_IMAGE_INFO_STRING = "CURRENT_IMAGE_INFO"; +const char *POSSIBLE_FILE_CORRUPTION_STRING = "POSSIBLE_FILE_CORRUPTION"; const char *NO_VALID_SENSOR_TEMPERATURE_STRING = "NO_VALID_SENSOR_TEMPERATURE"; const char *NO_HEALTHY_HEATER_AVAILABLE_STRING = "NO_HEALTHY_HEATER_AVAILABLE"; const char *SYRLINKS_OVERHEATING_STRING = "SYRLINKS_OVERHEATING"; @@ -433,6 +436,10 @@ const char *translateEvents(Event event) { return MSG_QUEUE_ERROR_STRING; case (10802): return SERIALIZATION_ERROR_STRING; + case (10803): + return FILESTORE_ERROR_STRING; + case (10804): + return FILENAME_TOO_LARGE_ERROR_STRING; case (11200): return SAFE_RATE_VIOLATION_STRING; case (11201): @@ -760,18 +767,20 @@ const char *translateEvents(Event event) { case (14006): return CURRENT_IMAGE_INFO_STRING; case (14100): + return POSSIBLE_FILE_CORRUPTION_STRING; + case (14200): return NO_VALID_SENSOR_TEMPERATURE_STRING; - case (14101): + case (14201): return NO_HEALTHY_HEATER_AVAILABLE_STRING; - case (14102): + case (14202): return SYRLINKS_OVERHEATING_STRING; - case (14103): + case (14203): return PLOC_OVERHEATING_STRING; - case (14104): + case (14204): return OBC_OVERHEATING_STRING; - case (14105): + case (14205): return HPA_OVERHEATING_STRING; - case (14106): + case (14206): return PLPCDU_OVERHEATING_STRING; default: return "UNKNOWN_EVENT"; diff --git a/bsp_hosted/fsfwconfig/objects/translateObjects.cpp b/bsp_hosted/fsfwconfig/objects/translateObjects.cpp index 63e56ef2..be60d130 100644 --- a/bsp_hosted/fsfwconfig/objects/translateObjects.cpp +++ b/bsp_hosted/fsfwconfig/objects/translateObjects.cpp @@ -1,8 +1,8 @@ /** * @brief Auto-generated object translation file. * @details - * Contains 148 translations. - * Generated on: 2023-02-23 15:39:20 + * Contains 154 translations. + * Generated on: 2023-02-24 16:57:00 */ #include "translateObjects.h" @@ -112,6 +112,7 @@ const char *PUS_SERVICE_5_EVENT_REPORTING_STRING = "PUS_SERVICE_5_EVENT_REPORTIN const char *PUS_SERVICE_8_FUNCTION_MGMT_STRING = "PUS_SERVICE_8_FUNCTION_MGMT"; const char *PUS_SERVICE_9_TIME_MGMT_STRING = "PUS_SERVICE_9_TIME_MGMT"; const char *PUS_SERVICE_11_TC_SCHEDULER_STRING = "PUS_SERVICE_11_TC_SCHEDULER"; +const char *PUS_SERVICE_15_TM_STORAGE_STRING = "PUS_SERVICE_15_TM_STORAGE"; const char *PUS_SERVICE_17_TEST_STRING = "PUS_SERVICE_17_TEST"; const char *PUS_SERVICE_20_PARAMETERS_STRING = "PUS_SERVICE_20_PARAMETERS"; const char *PUS_SERVICE_200_MODE_MGMT_STRING = "PUS_SERVICE_200_MODE_MGMT"; @@ -150,6 +151,11 @@ const char *ACS_SUBSYSTEM_STRING = "ACS_SUBSYSTEM"; const char *PL_SUBSYSTEM_STRING = "PL_SUBSYSTEM"; const char *TCS_SUBSYSTEM_STRING = "TCS_SUBSYSTEM"; const char *COM_SUBSYSTEM_STRING = "COM_SUBSYSTEM"; +const char *MISC_TM_STORE_STRING = "MISC_TM_STORE"; +const char *OK_TM_STORE_STRING = "OK_TM_STORE"; +const char *NOT_OK_TM_STORE_STRING = "NOT_OK_TM_STORE"; +const char *HK_TM_STORE_STRING = "HK_TM_STORE"; +const char *CFDP_TM_STORE_STRING = "CFDP_TM_STORE"; const char *CCSDS_IP_CORE_BRIDGE_STRING = "CCSDS_IP_CORE_BRIDGE"; const char *THERMAL_TEMP_INSERTER_STRING = "THERMAL_TEMP_INSERTER"; const char *DUMMY_INTERFACE_STRING = "DUMMY_INTERFACE"; @@ -369,6 +375,8 @@ const char *translateObject(object_id_t object) { return PUS_SERVICE_9_TIME_MGMT_STRING; case 0x53000011: return PUS_SERVICE_11_TC_SCHEDULER_STRING; + case 0x53000015: + return PUS_SERVICE_15_TM_STORAGE_STRING; case 0x53000017: return PUS_SERVICE_17_TEST_STRING; case 0x53000020: @@ -445,6 +453,16 @@ const char *translateObject(object_id_t object) { return TCS_SUBSYSTEM_STRING; case 0x73010004: return COM_SUBSYSTEM_STRING; + case 0x73020001: + return MISC_TM_STORE_STRING; + case 0x73020002: + return OK_TM_STORE_STRING; + case 0x73020003: + return NOT_OK_TM_STORE_STRING; + case 0x73020004: + return HK_TM_STORE_STRING; + case 0x73030000: + return CFDP_TM_STORE_STRING; case 0x73500000: return CCSDS_IP_CORE_BRIDGE_STRING; case 0x90000003: diff --git a/bsp_hosted/scheduling.cpp b/bsp_hosted/scheduling.cpp index 270de82e..9f859b6c 100644 --- a/bsp_hosted/scheduling.cpp +++ b/bsp_hosted/scheduling.cpp @@ -95,46 +95,43 @@ void scheduling::initTasks() { sif::error << "Add component UDP Polling failed" << std::endl; } - /* PUS Services */ - PeriodicTaskIF* pusVerification = factory->createPeriodicTask( - "PUS_VERIF", 40, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.200, missedDeadlineFunc); - result = pusVerification->addComponent(objects::PUS_SERVICE_1_VERIFICATION); + PeriodicTaskIF* pusHighPrio = factory->createPeriodicTask( + "PUS_HIGH_PRIO", 60, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.200, missedDeadlineFunc); + result = pusHighPrio->addComponent(objects::PUS_SERVICE_1_VERIFICATION); if (result != returnvalue::OK) { sif::error << "Object add component failed" << std::endl; } - - PeriodicTaskIF* eventHandling = factory->createPeriodicTask( - "EVENTS", 60, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.200, missedDeadlineFunc); - result = eventHandling->addComponent(objects::EVENT_MANAGER); + result = pusHighPrio->addComponent(objects::EVENT_MANAGER); if (result != returnvalue::OK) { - scheduling::printAddObjectError("EVENT_MNGR", objects::EVENT_MANAGER); + scheduling::printAddObjectError("EVENT_MGMT", objects::EVENT_MANAGER); } - result = eventHandling->addComponent(objects::PUS_SERVICE_5_EVENT_REPORTING); + result = pusHighPrio->addComponent(objects::PUS_SERVICE_5_EVENT_REPORTING); if (result != returnvalue::OK) { scheduling::printAddObjectError("PUS5", objects::PUS_SERVICE_5_EVENT_REPORTING); } - - PeriodicTaskIF* pusHighPrio = factory->createPeriodicTask( - "PUS_HIGH_PRIO", 50, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.200, missedDeadlineFunc); - result = pusHighPrio->addComponent(objects::PUS_SERVICE_2_DEVICE_ACCESS); - if (result != returnvalue::OK) { - scheduling::printAddObjectError("PUS2", objects::PUS_SERVICE_2_DEVICE_ACCESS); - } result = pusHighPrio->addComponent(objects::PUS_SERVICE_9_TIME_MGMT); if (result != returnvalue::OK) { scheduling::printAddObjectError("PUS9", objects::PUS_SERVICE_9_TIME_MGMT); } + + PeriodicTaskIF* pusMedPrio = factory->createPeriodicTask( + "PUS_MED_PRIO", 40, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.8, missedDeadlineFunc); + result = pusHighPrio->addComponent(objects::PUS_SERVICE_2_DEVICE_ACCESS); + if (result != returnvalue::OK) { + scheduling::printAddObjectError("PUS2", objects::PUS_SERVICE_2_DEVICE_ACCESS); + } result = pusHighPrio->addComponent(objects::PUS_SERVICE_3_HOUSEKEEPING); if (result != returnvalue::OK) { scheduling::printAddObjectError("PUS3", objects::PUS_SERVICE_3_HOUSEKEEPING); } - - PeriodicTaskIF* pusMedPrio = factory->createPeriodicTask( - "PUS_MED_PRIO", 40, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.8, missedDeadlineFunc); result = pusMedPrio->addComponent(objects::PUS_SERVICE_8_FUNCTION_MGMT); if (result != returnvalue::OK) { scheduling::printAddObjectError("PUS8", objects::PUS_SERVICE_8_FUNCTION_MGMT); } + result = pusMedPrio->addComponent(objects::PUS_SERVICE_15_TM_STORAGE); + if (result != returnvalue::OK) { + scheduling::printAddObjectError("PUS15", objects::PUS_SERVICE_15_TM_STORAGE); + } result = pusMedPrio->addComponent(objects::PUS_SERVICE_200_MODE_MGMT); if (result != returnvalue::OK) { scheduling::printAddObjectError("PUS200", objects::PUS_SERVICE_200_MODE_MGMT); @@ -143,10 +140,7 @@ void scheduling::initTasks() { if (result != returnvalue::OK) { scheduling::printAddObjectError("PUS20", objects::PUS_SERVICE_20_PARAMETERS); } - - PeriodicTaskIF* pusLowPrio = factory->createPeriodicTask( - "PUS_LOW_PRIO", 30, PeriodicTaskIF::MINIMUM_STACK_SIZE, 1.6, missedDeadlineFunc); - result = pusLowPrio->addComponent(objects::PUS_SERVICE_17_TEST); + result = pusMedPrio->addComponent(objects::PUS_SERVICE_17_TEST); if (result != returnvalue::OK) { scheduling::printAddObjectError("PUS17", objects::PUS_SERVICE_17_TEST); } @@ -220,11 +214,8 @@ void scheduling::initTasks() { udpPollingTask->startTask(); tcpPollingTask->startTask(); - pusVerification->startTask(); - eventHandling->startTask(); pusHighPrio->startTask(); pusMedPrio->startTask(); - pusLowPrio->startTask(); pstTask->startTask(); thermalTask->startTask(); diff --git a/bsp_linux_board/CMakeLists.txt b/bsp_linux_board/CMakeLists.txt index 39f06401..9e3ec023 100644 --- a/bsp_linux_board/CMakeLists.txt +++ b/bsp_linux_board/CMakeLists.txt @@ -1,5 +1,5 @@ target_sources(${OBSW_NAME} PUBLIC InitMission.cpp main.cpp gpioInit.cpp - ObjectFactory.cpp RPiSdCardManager.cpp) + ObjectFactory.cpp) add_subdirectory(boardconfig) add_subdirectory(boardtest) diff --git a/bsp_linux_board/ObjectFactory.cpp b/bsp_linux_board/ObjectFactory.cpp index 53db052c..95ea87bb 100644 --- a/bsp_linux_board/ObjectFactory.cpp +++ b/bsp_linux_board/ObjectFactory.cpp @@ -82,7 +82,7 @@ void ObjectFactory::produce(void* args) { #endif #if OBSW_ADD_SCEX_DEVICE == 1 - auto* sdcMan = new RPiSdCardManager("/tmp"); + auto* sdcMan = new DummySdCardManager("/tmp"); createScexComponents(uart::DEV, pwrSwitcher, *sdcMan, true, std::nullopt); #endif diff --git a/bsp_linux_board/RPiSdCardManager.cpp b/bsp_linux_board/RPiSdCardManager.cpp deleted file mode 100644 index dfcae8da..00000000 --- a/bsp_linux_board/RPiSdCardManager.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "RPiSdCardManager.h" - -RPiSdCardManager::RPiSdCardManager(std::string prefix) : prefix(std::move(prefix)) {} - -const std::string& RPiSdCardManager::getCurrentMountPrefix() const { return prefix; } - -bool RPiSdCardManager::isSdCardUsable(sd::SdCard sdCard) { return true; } - -std::optional RPiSdCardManager::getPreferredSdCard() const { return std::nullopt; } - -void RPiSdCardManager::setActiveSdCard(sd::SdCard sdCard) {} - -std::optional RPiSdCardManager::getActiveSdCard() const { return std::nullopt; } diff --git a/bsp_q7s/core/CoreController.cpp b/bsp_q7s/core/CoreController.cpp index cce653ee..7ee3191b 100644 --- a/bsp_q7s/core/CoreController.cpp +++ b/bsp_q7s/core/CoreController.cpp @@ -49,6 +49,8 @@ CoreController::CoreController(object_id_t objectId) } getCurrentBootCopy(CURRENT_CHIP, CURRENT_COPY); + + initClockFromTimeFile(); } catch (const std::filesystem::filesystem_error &e) { sif::error << "CoreController::CoreController: Failed with exception " << e.what() << std::endl; } @@ -159,6 +161,9 @@ ReturnValue_t CoreController::initializeAfterTaskCreation() { } sdcMan->setActiveSdCard(sdInfo.active); currMntPrefix = sdcMan->getCurrentMountPrefix(); + if (currMntPrefix == "") { + return ObjectManagerIF::CHILD_INIT_FAILED; + } if (BLOCKING_SD_INIT) { result = initSdCardBlocking(); if (result != returnvalue::OK and result != SdCardManager::ALREADY_MOUNTED) { @@ -1266,10 +1271,12 @@ void CoreController::performMountedSdCardOperations() { if (result != returnvalue::OK) { sif::warning << "CoreController::CoreController: Boot copy init" << std::endl; } - initClockFromTimeFile(); + if (not timeFileInitDone) { + initClockFromTimeFile(); + } performRebootFileHandling(false); } - timeFileHandler(); + backupTimeFileHandler(); }; bool someSdCardActive = false; if (sdInfo.active == sd::SdCard::SLOT_0 and sdcMan->isSdCardUsable(sd::SdCard::SLOT_0)) { @@ -1783,7 +1790,7 @@ void CoreController::setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::C rewriteRebootFile(rebootFile); } -ReturnValue_t CoreController::timeFileHandler() { +ReturnValue_t CoreController::backupTimeFileHandler() { // Always set time. We could only set it if it is updated by GPS, but then the backup time would // become obsolete on GPS problems. if (opDivider10.check()) { @@ -1793,14 +1800,14 @@ ReturnValue_t CoreController::timeFileHandler() { if (result != returnvalue::OK) { return result; } - std::string fileName = currMntPrefix + TIME_FILE; + std::string fileName = currMntPrefix + BACKUP_TIME_FILE; std::ofstream timeFile(fileName); if (not timeFile.good()) { sif::error << "CoreController::timeFileHandler: Error opening time file: " << strerror(errno) << std::endl; return returnvalue::FAILED; } - timeFile << "UNIX SECONDS: " << currentTime.tv_sec << std::endl; + timeFile << "UNIX SECONDS: " << currentTime.tv_sec + BOOT_OFFSET_SECONDS << std::endl; } return returnvalue::OK; } @@ -1808,8 +1815,8 @@ ReturnValue_t CoreController::timeFileHandler() { ReturnValue_t CoreController::initClockFromTimeFile() { using namespace GpsHyperion; using namespace std; - std::string fileName = currMntPrefix + TIME_FILE; - if (std::filesystem::exists(fileName) and + std::string fileName = currMntPrefix + BACKUP_TIME_FILE; + if (sdcMan->isSdCardUsable(std::nullopt) and std::filesystem::exists(fileName) and ((gpsFix == FixMode::UNKNOWN or gpsFix == FixMode::NOT_SEEN) or not utility::timeSanityCheck())) { ifstream timeFile(fileName); @@ -1837,6 +1844,7 @@ ReturnValue_t CoreController::initClockFromTimeFile() { sif::info << "Setting system time from time files: " << std::put_time(time, "%c %Z") << std::endl; #endif + timeFileInitDone = true; return Clock::setClock(¤tTime); } return returnvalue::OK; diff --git a/bsp_q7s/core/CoreController.h b/bsp_q7s/core/CoreController.h index 65ee20ef..1376c0a8 100644 --- a/bsp_q7s/core/CoreController.h +++ b/bsp_q7s/core/CoreController.h @@ -58,13 +58,14 @@ class CoreController : public ExtendedControllerBase { static constexpr char VERSION_FILE_NAME[] = "version.txt"; static constexpr char REBOOT_FILE_NAME[] = "reboot.txt"; - static constexpr char TIME_FILE_NAME[] = "time.txt"; + static constexpr char TIME_FILE_NAME[] = "time_backup.txt"; const std::string VERSION_FILE = "/" + std::string(CONF_FOLDER) + "/" + std::string(VERSION_FILE_NAME); const std::string REBOOT_FILE = "/" + std::string(CONF_FOLDER) + "/" + std::string(REBOOT_FILE_NAME); - const std::string TIME_FILE = "/" + std::string(CONF_FOLDER) + "/" + std::string(TIME_FILE_NAME); + const std::string BACKUP_TIME_FILE = + "/" + std::string(CONF_FOLDER) + "/" + std::string(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"; @@ -160,6 +161,7 @@ class CoreController : public ExtendedControllerBase { bool sdInitFinished() const; private: + static constexpr uint32_t BOOT_OFFSET_SECONDS = 15; static constexpr MutexIF::TimeoutType TIMEOUT_TYPE = MutexIF::TimeoutType::WAITING; static constexpr uint32_t MUTEX_TIMEOUT = 20; // Designated value for rechecking FIFO open @@ -221,6 +223,7 @@ class CoreController : public ExtendedControllerBase { RebootFile rebootFile = {}; std::string currMntPrefix; + bool timeFileInitDone = false; bool performOneShotSdCardOpsSwitch = false; uint8_t shortSdCardCdCounter = 0; #if OBSW_THREAD_TRACING == 1 @@ -258,7 +261,7 @@ class CoreController : public ExtendedControllerBase { ReturnValue_t initClockFromTimeFile(); ReturnValue_t performSdCardCheck(); - ReturnValue_t timeFileHandler(); + ReturnValue_t backupTimeFileHandler(); ReturnValue_t initBootCopyFile(); ReturnValue_t initWatchdogFifo(); ReturnValue_t initSdCardBlocking(); diff --git a/bsp_q7s/core/scheduling.cpp b/bsp_q7s/core/scheduling.cpp index 6dc7fceb..7647af8b 100644 --- a/bsp_q7s/core/scheduling.cpp +++ b/bsp_q7s/core/scheduling.cpp @@ -187,14 +187,12 @@ void scheduling::initTasks() { } #endif -#if OBSW_ADD_GPS_CTRL == 1 PeriodicTaskIF* gpsTask = factory->createPeriodicTask( "GPS_TASK", 50, PeriodicTaskIF::MINIMUM_STACK_SIZE * 2, 0.4, missedDeadlineFunc); result = gpsTask->addComponent(objects::GPS_CONTROLLER); if (result != returnvalue::OK) { scheduling::printAddObjectError("GPS_CTRL", objects::GPS_CONTROLLER); } -#endif /* OBSW_ADD_GPS_CTRL */ #if OBSW_ADD_RW == 1 PeriodicTaskIF* rwPolling = factory->createPeriodicTask( @@ -378,9 +376,7 @@ void scheduling::initTasks() { #if OBSW_ADD_RW == 1 rwPolling->startTask(); #endif -#if OBSW_ADD_GPS_CTRL == 1 gpsTask->startTask(); -#endif acsSysTask->startTask(); if (not tcsSystemTask->isEmpty()) { tcsSystemTask->startTask(); diff --git a/bsp_q7s/em/emObjectFactory.cpp b/bsp_q7s/em/emObjectFactory.cpp index 1ab86196..8364533a 100644 --- a/bsp_q7s/em/emObjectFactory.cpp +++ b/bsp_q7s/em/emObjectFactory.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "OBSWConfig.h" #include "bsp_q7s/core/CoreController.h" @@ -22,7 +23,8 @@ void ObjectFactory::produce(void* args) { HealthTableIF* healthTable = nullptr; PusTmFunnel* pusFunnel = nullptr; CfdpTmFunnel* cfdpFunnel = nullptr; - ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel); + ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel, + *SdCardManager::instance()); LinuxLibgpioIF* gpioComIF = nullptr; SerialComIF* uartComIF = nullptr; diff --git a/bsp_q7s/fmObjectFactory.cpp b/bsp_q7s/fmObjectFactory.cpp index d640ac35..36e9df64 100644 --- a/bsp_q7s/fmObjectFactory.cpp +++ b/bsp_q7s/fmObjectFactory.cpp @@ -18,7 +18,8 @@ void ObjectFactory::produce(void* args) { HealthTableIF* healthTable = nullptr; PusTmFunnel* pusFunnel = nullptr; CfdpTmFunnel* cfdpFunnel = nullptr; - ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel); + ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel, + *SdCardManager::instance()); LinuxLibgpioIF* gpioComIF = nullptr; SerialComIF* uartComIF = nullptr; diff --git a/bsp_q7s/fs/SdCardManager.cpp b/bsp_q7s/fs/SdCardManager.cpp index c0b1184e..9e5d05bc 100644 --- a/bsp_q7s/fs/SdCardManager.cpp +++ b/bsp_q7s/fs/SdCardManager.cpp @@ -410,9 +410,12 @@ ReturnValue_t SdCardManager::updateSdCardStateFile() { return result; } -const std::string& SdCardManager::getCurrentMountPrefix() const { +const char* SdCardManager::getCurrentMountPrefix() const { MutexGuard mg(mutex); - return currentPrefix; + if (currentPrefix.has_value()) { + return currentPrefix.value().c_str(); + } + return nullptr; } SdCardManager::OpStatus SdCardManager::checkCurrentOp(Operations& currentOp) { diff --git a/bsp_q7s/fs/SdCardManager.h b/bsp_q7s/fs/SdCardManager.h index 749cae62..77055589 100644 --- a/bsp_q7s/fs/SdCardManager.h +++ b/bsp_q7s/fs/SdCardManager.h @@ -187,7 +187,7 @@ class SdCardManager : public SystemObject, public SdCardMountedIF { * @param prefSdCardPtr * @return */ - const std::string& getCurrentMountPrefix() const override; + const char* getCurrentMountPrefix() const override; OpStatus checkCurrentOp(Operations& currentOp); @@ -232,7 +232,7 @@ class SdCardManager : public SystemObject, public SdCardMountedIF { void processSdStatusLine(SdStatePair& active, std::string& line, uint8_t& idx, sd::SdCard& currentSd); - std::string currentPrefix; + std::optional currentPrefix; static SdCardManager* INSTANCE; }; diff --git a/bsp_q7s/fs/helpers.cpp b/bsp_q7s/fs/helpers.cpp index f1f31eba..19c2f67e 100644 --- a/bsp_q7s/fs/helpers.cpp +++ b/bsp_q7s/fs/helpers.cpp @@ -3,6 +3,9 @@ std::filesystem::path fshelpers::getPrefixedPath(SdCardManager &man, std::filesystem::path pathWihtoutPrefix) { auto prefix = man.getCurrentMountPrefix(); + if (prefix == nullptr) { + return pathWihtoutPrefix; + } auto resPath = prefix / pathWihtoutPrefix; return resPath; } diff --git a/common/config/eive/definitions.h b/common/config/eive/definitions.h index edeb3765..4f2c948e 100644 --- a/common/config/eive/definitions.h +++ b/common/config/eive/definitions.h @@ -61,8 +61,8 @@ static constexpr uint32_t SCHED_BLOCK_1_SUS_READ_MS = 15; static constexpr uint32_t SCHED_BLOCK_2_SENSOR_READ_MS = 30; static constexpr uint32_t SCHED_BLOCK_3_READ_IMTQ_MGM_MS = 42; static constexpr uint32_t SCHED_BLOCK_4_ACS_CTRL_MS = 45; -static constexpr uint32_t SCHED_BLOCK_5_ACTUATOR_MS = 50; -static constexpr uint32_t SCHED_BLOCK_6_IMTQ_BLOCK_2_MS = 90; +static constexpr uint32_t SCHED_BLOCK_5_ACTUATOR_MS = 55; +static constexpr uint32_t SCHED_BLOCK_6_IMTQ_BLOCK_2_MS = 95; static constexpr uint32_t SCHED_BLOCK_RTD = 150; static constexpr uint32_t SCHED_BLOCK_7_RW_READ_MS = 300; diff --git a/common/config/eive/eventSubsystemIds.h b/common/config/eive/eventSubsystemIds.h index 923a06ad..bbe9569a 100644 --- a/common/config/eive/eventSubsystemIds.h +++ b/common/config/eive/eventSubsystemIds.h @@ -38,6 +38,7 @@ enum : uint8_t { CORE = 140, TCS_CONTROLLER = 141, COM_SUBSYSTEM = 142, + PERSISTENT_TM_STORE = 143, COMMON_SUBSYSTEM_ID_END }; diff --git a/common/config/eive/objects.h b/common/config/eive/objects.h index 105c78cc..08f07d3d 100644 --- a/common/config/eive/objects.h +++ b/common/config/eive/objects.h @@ -152,6 +152,11 @@ enum commonObjects : uint32_t { CFDP_TM_FUNNEL = 0x73000102, CFDP_HANDLER = 0x73000205, CFDP_DISTRIBUTOR = 0x73000206, + MISC_TM_STORE = 0x73020001, + OK_TM_STORE = 0x73020002, + NOT_OK_TM_STORE = 0x73020003, + HK_TM_STORE = 0x73020004, + CFDP_TM_STORE = 0x73030000, // Other stuff THERMAL_TEMP_INSERTER = 0x90000003, diff --git a/common/config/eive/resultClassIds.h b/common/config/eive/resultClassIds.h index e4dfb927..310d9e0b 100644 --- a/common/config/eive/resultClassIds.h +++ b/common/config/eive/resultClassIds.h @@ -35,14 +35,13 @@ enum commonClassIds : uint8_t { SA_DEPL_HANDLER, // SADPL MPSOC_RETURN_VALUES_IF, // MPSOCRTVIF SUPV_RETURN_VALUES_IF, // SPVRTVIF - ACS_KALMAN, // ACSKAL + ACS_CTRL, // ACSCTRL + ACS_MEKF, // ACSMEKF ACS_SAFE, // ACSSAF ACS_PTG, // ACSPTG ACS_DETUMBLE, // ACSDTB - ACS_MEKF, // ACSMEK COMMON_CLASS_ID_END // [EXPORT] : [END] }; - } #endif /* COMMON_CONFIG_COMMONCLASSIDS_H_ */ diff --git a/common/config/tmtc/pusIds.h b/common/config/tmtc/pusIds.h index 0891992d..b44d7e20 100644 --- a/common/config/tmtc/pusIds.h +++ b/common/config/tmtc/pusIds.h @@ -12,6 +12,7 @@ enum Ids { PUS_SERVICE_8 = 8, PUS_SERVICE_9 = 9, PUS_SERVICE_11 = 11, + PUS_SERVICE_15 = 15, PUS_SERVICE_17 = 17, PUS_SERVICE_19 = 19, PUS_SERVICE_20 = 20, diff --git a/dummies/CMakeLists.txt b/dummies/CMakeLists.txt index 810cc048..41cb3328 100644 --- a/dummies/CMakeLists.txt +++ b/dummies/CMakeLists.txt @@ -20,6 +20,7 @@ target_sources( GyroL3GD20Dummy.cpp MgmLIS3MDLDummy.cpp PlPcduDummy.cpp + ExecutableComIfDummy.cpp ScexDummy.cpp CoreControllerDummy.cpp PlocMpsocDummy.cpp diff --git a/dummies/ExecutableComIfDummy.cpp b/dummies/ExecutableComIfDummy.cpp new file mode 100644 index 00000000..becb4a73 --- /dev/null +++ b/dummies/ExecutableComIfDummy.cpp @@ -0,0 +1,27 @@ +#include + +ExecutableComIfDummy::ExecutableComIfDummy(object_id_t objectId) : SystemObject(objectId) {} + +ReturnValue_t ExecutableComIfDummy::initializeInterface(CookieIF *cookie) { + return returnvalue::OK; +} + +ReturnValue_t ExecutableComIfDummy::sendMessage(CookieIF *cookie, const uint8_t *sendData, + size_t sendLen) { + return returnvalue::OK; +} + +ReturnValue_t ExecutableComIfDummy::getSendSuccess(CookieIF *cookie) { return returnvalue::OK; } + +ReturnValue_t ExecutableComIfDummy::requestReceiveMessage(CookieIF *cookie, size_t requestLen) { + return returnvalue::OK; +} + +ReturnValue_t ExecutableComIfDummy::performOperation(uint8_t operationCode) { + return returnvalue::OK; +} + +ReturnValue_t ExecutableComIfDummy::readReceivedMessage(CookieIF *cookie, uint8_t **buffer, + size_t *size) { + return returnvalue::OK; +} diff --git a/dummies/ExecutableComIfDummy.h b/dummies/ExecutableComIfDummy.h new file mode 100644 index 00000000..80e667d7 --- /dev/null +++ b/dummies/ExecutableComIfDummy.h @@ -0,0 +1,21 @@ +#ifndef DUMMIES_EXECUTABLECOMIFDUMMY_H_ +#define DUMMIES_EXECUTABLECOMIFDUMMY_H_ + +#include +#include +#include + +class ExecutableComIfDummy : public ExecutableObjectIF, + public DeviceCommunicationIF, + public SystemObject { + public: + ExecutableComIfDummy(object_id_t objectId); + ReturnValue_t performOperation(uint8_t operationCode = 0) override; + ReturnValue_t initializeInterface(CookieIF *cookie) override; + ReturnValue_t sendMessage(CookieIF *cookie, const uint8_t *sendData, size_t sendLen) override; + ReturnValue_t getSendSuccess(CookieIF *cookie) override; + ReturnValue_t requestReceiveMessage(CookieIF *cookie, size_t requestLen) override; + ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, size_t *size) override; +}; + +#endif /* DUMMIES_EXECUTABLECOMIFDUMMY_H_ */ diff --git a/dummies/GpsCtrlDummy.cpp b/dummies/GpsCtrlDummy.cpp index e69261b4..4ae8dc6f 100644 --- a/dummies/GpsCtrlDummy.cpp +++ b/dummies/GpsCtrlDummy.cpp @@ -1,6 +1,7 @@ #include "GpsCtrlDummy.h" -GpsCtrlDummy::GpsCtrlDummy(object_id_t objectId) : ExtendedControllerBase(objectId, 20) {} +GpsCtrlDummy::GpsCtrlDummy(object_id_t objectId) + : ExtendedControllerBase(objectId, 20), gpsSet(this) {} ReturnValue_t GpsCtrlDummy::handleCommandMessage(CommandMessage* message) { return returnvalue::OK; @@ -13,9 +14,23 @@ ReturnValue_t GpsCtrlDummy::checkModeCommand(Mode_t mode, Submode_t submode, return returnvalue::OK; } +LocalPoolDataSetBase* GpsCtrlDummy::getDataSetHandle(sid_t sid) { return &gpsSet; } + ReturnValue_t GpsCtrlDummy::initializeLocalDataPool(localpool::DataPool& localDataPoolMap, LocalDataPoolManager& poolManager) { + localDataPoolMap.emplace(GpsHyperion::ALTITUDE, new PoolEntry({537222.3469}, true)); + localDataPoolMap.emplace(GpsHyperion::LONGITUDE, new PoolEntry({-8.8579}, true)); + localDataPoolMap.emplace(GpsHyperion::LATITUDE, new PoolEntry({49.5952}, true)); + localDataPoolMap.emplace(GpsHyperion::SPEED, new PoolEntry({0})); + localDataPoolMap.emplace(GpsHyperion::YEAR, new PoolEntry({2023}, true)); + localDataPoolMap.emplace(GpsHyperion::MONTH, new PoolEntry({5}, true)); + localDataPoolMap.emplace(GpsHyperion::DAY, new PoolEntry({16}, true)); + localDataPoolMap.emplace(GpsHyperion::HOURS, new PoolEntry({1}, true)); + localDataPoolMap.emplace(GpsHyperion::MINUTES, new PoolEntry({0}, true)); + localDataPoolMap.emplace(GpsHyperion::SECONDS, new PoolEntry({0}, true)); + localDataPoolMap.emplace(GpsHyperion::UNIX_SECONDS, new PoolEntry({1684191600}, true)); + localDataPoolMap.emplace(GpsHyperion::SATS_IN_USE, new PoolEntry()); + localDataPoolMap.emplace(GpsHyperion::SATS_IN_VIEW, new PoolEntry()); + localDataPoolMap.emplace(GpsHyperion::FIX_MODE, new PoolEntry()); return returnvalue::OK; } - -LocalPoolDataSetBase* GpsCtrlDummy::getDataSetHandle(sid_t sid) { return nullptr; } diff --git a/dummies/GpsCtrlDummy.h b/dummies/GpsCtrlDummy.h index 128a9a85..4b7fc1bf 100644 --- a/dummies/GpsCtrlDummy.h +++ b/dummies/GpsCtrlDummy.h @@ -2,12 +2,15 @@ #define DUMMIES_GPSCTRLDUMMY_H_ #include +#include class GpsCtrlDummy : public ExtendedControllerBase { public: GpsCtrlDummy(object_id_t objectId); private: + GpsPrimaryDataset gpsSet; + ReturnValue_t handleCommandMessage(CommandMessage* message) override; void performControlOperation() override; ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, diff --git a/dummies/PduDummy.cpp b/dummies/PduDummy.cpp index 1c26728c..42147222 100644 --- a/dummies/PduDummy.cpp +++ b/dummies/PduDummy.cpp @@ -3,7 +3,8 @@ #include PduDummy::PduDummy(object_id_t objectId, object_id_t comif, CookieIF *comCookie) - : DeviceHandlerBase(objectId, comif, comCookie) {} + : DeviceHandlerBase(objectId, comif, comCookie), + coreHk(this, static_cast(P60System::SetIds::CORE)) {} PduDummy::~PduDummy() {} @@ -38,5 +39,7 @@ uint32_t PduDummy::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return ReturnValue_t PduDummy::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { localDataPoolMap.emplace(PDU::pool::PDU_TEMPERATURE, new PoolEntry({0})); + localDataPoolMap.emplace(PDU::pool::PDU_VOLTAGES, &pduVoltages); + localDataPoolMap.emplace(PDU::pool::PDU_CURRENTS, &pduCurrents); return returnvalue::OK; } diff --git a/dummies/PduDummy.h b/dummies/PduDummy.h index 3e193e7c..751896cd 100644 --- a/dummies/PduDummy.h +++ b/dummies/PduDummy.h @@ -3,6 +3,8 @@ #include +#include "mission/devices/devicedefinitions/GomspaceDefinitions.h" + class PduDummy : public DeviceHandlerBase { public: static const DeviceCommandId_t SIMPLE_COMMAND = 1; @@ -15,6 +17,10 @@ class PduDummy : public DeviceHandlerBase { virtual ~PduDummy(); protected: + PDU::PduCoreHk coreHk; + PoolEntry pduVoltages = PoolEntry(9); + PoolEntry pduCurrents = PoolEntry(9); + void doStartUp() override; void doShutDown() override; ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override; diff --git a/dummies/RwDummy.cpp b/dummies/RwDummy.cpp index 54e7ac83..d90f3f7c 100644 --- a/dummies/RwDummy.cpp +++ b/dummies/RwDummy.cpp @@ -41,7 +41,7 @@ ReturnValue_t RwDummy::initializeLocalDataPool(localpool::DataPool &localDataPoo localDataPoolMap.emplace(rws::CURR_SPEED, new PoolEntry({0})); localDataPoolMap.emplace(rws::REFERENCE_SPEED, new PoolEntry({0})); - localDataPoolMap.emplace(rws::STATE, new PoolEntry({0})); + localDataPoolMap.emplace(rws::STATE, new PoolEntry({1}, true)); localDataPoolMap.emplace(rws::CLC_MODE, new PoolEntry({0})); localDataPoolMap.emplace(rws::LAST_RESET_STATUS, new PoolEntry({0})); diff --git a/dummies/SusDummy.cpp b/dummies/SusDummy.cpp index c0aed6dd..7c271b21 100644 --- a/dummies/SusDummy.cpp +++ b/dummies/SusDummy.cpp @@ -37,7 +37,7 @@ ReturnValue_t SusDummy::initializeLocalDataPool(localpool::DataPool &localDataPo LocalDataPoolManager &poolManager) { localDataPoolMap.emplace(SUS::SusPoolIds::TEMPERATURE_C, new PoolEntry({0}, 1, true)); localDataPoolMap.emplace(SUS::SusPoolIds::CHANNEL_VEC, - new PoolEntry({0, 0, 0, 0, 0, 0}, true)); + new PoolEntry({2603, 781, 2760, 2048, 4056, 0}, true)); return returnvalue::OK; } diff --git a/dummies/helpers.cpp b/dummies/helpers.cpp index a5b84451..f93a8127 100644 --- a/dummies/helpers.cpp +++ b/dummies/helpers.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -45,7 +46,7 @@ void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpio new CoreControllerDummy(objects::CORE_CONTROLLER); } if (cfg.addRtdComIFDummy) { - new ComIFDummy(objects::SPI_RTD_COM_IF); + new ExecutableComIfDummy(objects::SPI_RTD_COM_IF); } std::array rwIds = {objects::RW1, objects::RW2, objects::RW3, objects::RW4}; std::array rws; diff --git a/fsfw b/fsfw index bd208038..bdfe31db 160000 --- a/fsfw +++ b/fsfw @@ -1 +1 @@ -Subproject commit bd208038dd85a94dce8c763397ad5ac7eae76402 +Subproject commit bdfe31dba48039b60fe700e7d03bfb95e9549688 diff --git a/generators/.gitignore b/generators/.gitignore index 889fd273..c5e80e62 100644 --- a/generators/.gitignore +++ b/generators/.gitignore @@ -1,2 +1,4 @@ .~lock* /venv +/.idea/* +!/.idea/runConfigurations diff --git a/generators/bsp_hosted_events.csv b/generators/bsp_hosted_events.csv index 4542daa6..6d26ed51 100644 --- a/generators/bsp_hosted_events.csv +++ b/generators/bsp_hosted_events.csv @@ -84,6 +84,8 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 10800;0x2a30;STORE_ERROR;LOW;No description;fsfw/src/fsfw/cfdp/handler/defs.h 10801;0x2a31;MSG_QUEUE_ERROR;LOW;No description;fsfw/src/fsfw/cfdp/handler/defs.h 10802;0x2a32;SERIALIZATION_ERROR;LOW;No description;fsfw/src/fsfw/cfdp/handler/defs.h +10803;0x2a33;FILESTORE_ERROR;LOW;No description;fsfw/src/fsfw/cfdp/handler/defs.h +10804;0x2a34;FILENAME_TOO_LARGE_ERROR;LOW;P1: Transaction step ID, P2: 0 for source file name, 1 for dest file name;fsfw/src/fsfw/cfdp/handler/defs.h 11200;0x2bc0;SAFE_RATE_VIOLATION;MEDIUM;No description;mission/acsDefs.h 11201;0x2bc1;SAFE_RATE_RECOVERY;MEDIUM;No description;mission/acsDefs.h 11202;0x2bc2;MULTIPLE_RW_INVALID;HIGH;No description;mission/acsDefs.h @@ -252,10 +254,11 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 14004;0x36b4;NO_SD_CARD_ACTIVE;HIGH;No SD card was active. Core controller will attempt to re-initialize a SD card.;bsp_q7s/core/CoreController.h 14005;0x36b5;VERSION_INFO;INFO;P1: Byte 0: Major, Byte 1: Minor, Byte 2: Patch, Byte 3: Has Git Hash P2: First four letters of Git SHA is the last byte of P1 is set.;bsp_q7s/core/CoreController.h 14006;0x36b6;CURRENT_IMAGE_INFO;INFO;P1: Current Chip, P2: Current Copy;bsp_q7s/core/CoreController.h -14100;0x3714;NO_VALID_SENSOR_TEMPERATURE;MEDIUM;No description;mission/controller/ThermalController.h -14101;0x3715;NO_HEALTHY_HEATER_AVAILABLE;MEDIUM;No description;mission/controller/ThermalController.h -14102;0x3716;SYRLINKS_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h -14103;0x3717;PLOC_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h -14104;0x3718;OBC_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h -14105;0x3719;HPA_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h -14106;0x371a;PLPCDU_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h +14100;0x3714;POSSIBLE_FILE_CORRUPTION;LOW;P1: Result code of TM packet parser. P2: Timestamp of possibly corrupt file as a unix timestamp.;mission/tmtc/PersistentTmStore.h +14200;0x3778;NO_VALID_SENSOR_TEMPERATURE;MEDIUM;No description;mission/controller/ThermalController.h +14201;0x3779;NO_HEALTHY_HEATER_AVAILABLE;MEDIUM;No description;mission/controller/ThermalController.h +14202;0x377a;SYRLINKS_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h +14203;0x377b;PLOC_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h +14204;0x377c;OBC_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h +14205;0x377d;HPA_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h +14206;0x377e;PLPCDU_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h diff --git a/generators/bsp_hosted_objects.csv b/generators/bsp_hosted_objects.csv index d4c5ca74..f6c11782 100644 --- a/generators/bsp_hosted_objects.csv +++ b/generators/bsp_hosted_objects.csv @@ -104,6 +104,7 @@ 0x53000008;PUS_SERVICE_8_FUNCTION_MGMT 0x53000009;PUS_SERVICE_9_TIME_MGMT 0x53000011;PUS_SERVICE_11_TC_SCHEDULER +0x53000015;PUS_SERVICE_15_TM_STORAGE 0x53000017;PUS_SERVICE_17_TEST 0x53000020;PUS_SERVICE_20_PARAMETERS 0x53000200;PUS_SERVICE_200_MODE_MGMT @@ -142,6 +143,11 @@ 0x73010002;PL_SUBSYSTEM 0x73010003;TCS_SUBSYSTEM 0x73010004;COM_SUBSYSTEM +0x73020001;MISC_TM_STORE +0x73020002;OK_TM_STORE +0x73020003;NOT_OK_TM_STORE +0x73020004;HK_TM_STORE +0x73030000;CFDP_TM_STORE 0x73500000;CCSDS_IP_CORE_BRIDGE 0x90000003;THERMAL_TEMP_INSERTER 0xCAFECAFE;DUMMY_INTERFACE diff --git a/generators/bsp_hosted_returnvalues.csv b/generators/bsp_hosted_returnvalues.csv index 4c6d37e8..86c3f699 100644 --- a/generators/bsp_hosted_returnvalues.csv +++ b/generators/bsp_hosted_returnvalues.csv @@ -49,16 +49,17 @@ Full ID (hex); Name; Description; Unique ID; Subsytem Name; File Path 0x4fa4;HEATER_MainSwitchSetTimeout;No description;164;HEATER_HANDLER;mission/devices/HeaterHandler.h 0x4fa5;HEATER_CommandAlreadyWaiting;No description;165;HEATER_HANDLER;mission/devices/HeaterHandler.h 0x60a0;CCSDS_CommandNotImplemented;Received action message with unknown action id;160;CCSDS_HANDLER;mission/tmtc/CcsdsIpCoreHandler.h -0x6a01;ACSSAF_SafectrlMekfInputInvalid;No description;1;ACS_SAFE;mission/controller/acs/control/SafeCtrl.h -0x6b01;ACSPTG_PtgctrlMekfInputInvalid;No description;1;ACS_PTG;mission/controller/acs/control/PtgCtrl.h -0x6c01;ACSDTB_DetumbleNoSensordata;No description;1;ACS_DETUMBLE;mission/controller/acs/control/Detumble.h -0x6902;ACSKAL_KalmanUninitialized;No description;2;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6903;ACSKAL_KalmanNoGyrData;No description;3;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6904;ACSKAL_KalmanNoModelVectors;No description;4;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6905;ACSKAL_KalmanNoSusMgmStrData;No description;5;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6906;ACSKAL_KalmanCovarianceInversionFailed;No description;6;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6907;ACSKAL_KalmanInitialized;No description;7;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6908;ACSKAL_KalmanRunning;No description;8;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6b01;ACSSAF_SafectrlMekfInputInvalid;No description;1;ACS_SAFE;mission/controller/acs/control/SafeCtrl.h +0x6c01;ACSPTG_PtgctrlMekfInputInvalid;No description;1;ACS_PTG;mission/controller/acs/control/PtgCtrl.h +0x6d01;ACSDTB_DetumbleNoSensordata;No description;1;ACS_DETUMBLE;mission/controller/acs/control/Detumble.h +0x6a02;ACSMEKF_MekfUninitialized;No description;2;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a03;ACSMEKF_MekfNoGyrData;No description;3;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a04;ACSMEKF_MekfNoModelVectors;No description;4;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a05;ACSMEKF_MekfNoSusMgmStrData;No description;5;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a06;ACSMEKF_MekfCovarianceInversionFailed;No description;6;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a07;ACSMEKF_MekfInitialized;No description;7;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a08;ACSMEKF_MekfRunning;No description;8;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6900;ACSCTRL_FileDeletionFailed;No description;0;ACS_CTRL;mission/controller/AcsController.h 0x4500;HSPI_OpeningFileFailed;No description;0;HAL_SPI;fsfw/src/fsfw_hal/linux/spi/SpiComIF.h 0x4501;HSPI_FullDuplexTransferFailed;No description;1;HAL_SPI;fsfw/src/fsfw_hal/linux/spi/SpiComIF.h 0x4502;HSPI_HalfDuplexTransferFailed;No description;2;HAL_SPI;fsfw/src/fsfw_hal/linux/spi/SpiComIF.h diff --git a/generators/bsp_q7s_events.csv b/generators/bsp_q7s_events.csv index 4542daa6..6d26ed51 100644 --- a/generators/bsp_q7s_events.csv +++ b/generators/bsp_q7s_events.csv @@ -84,6 +84,8 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 10800;0x2a30;STORE_ERROR;LOW;No description;fsfw/src/fsfw/cfdp/handler/defs.h 10801;0x2a31;MSG_QUEUE_ERROR;LOW;No description;fsfw/src/fsfw/cfdp/handler/defs.h 10802;0x2a32;SERIALIZATION_ERROR;LOW;No description;fsfw/src/fsfw/cfdp/handler/defs.h +10803;0x2a33;FILESTORE_ERROR;LOW;No description;fsfw/src/fsfw/cfdp/handler/defs.h +10804;0x2a34;FILENAME_TOO_LARGE_ERROR;LOW;P1: Transaction step ID, P2: 0 for source file name, 1 for dest file name;fsfw/src/fsfw/cfdp/handler/defs.h 11200;0x2bc0;SAFE_RATE_VIOLATION;MEDIUM;No description;mission/acsDefs.h 11201;0x2bc1;SAFE_RATE_RECOVERY;MEDIUM;No description;mission/acsDefs.h 11202;0x2bc2;MULTIPLE_RW_INVALID;HIGH;No description;mission/acsDefs.h @@ -252,10 +254,11 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 14004;0x36b4;NO_SD_CARD_ACTIVE;HIGH;No SD card was active. Core controller will attempt to re-initialize a SD card.;bsp_q7s/core/CoreController.h 14005;0x36b5;VERSION_INFO;INFO;P1: Byte 0: Major, Byte 1: Minor, Byte 2: Patch, Byte 3: Has Git Hash P2: First four letters of Git SHA is the last byte of P1 is set.;bsp_q7s/core/CoreController.h 14006;0x36b6;CURRENT_IMAGE_INFO;INFO;P1: Current Chip, P2: Current Copy;bsp_q7s/core/CoreController.h -14100;0x3714;NO_VALID_SENSOR_TEMPERATURE;MEDIUM;No description;mission/controller/ThermalController.h -14101;0x3715;NO_HEALTHY_HEATER_AVAILABLE;MEDIUM;No description;mission/controller/ThermalController.h -14102;0x3716;SYRLINKS_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h -14103;0x3717;PLOC_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h -14104;0x3718;OBC_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h -14105;0x3719;HPA_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h -14106;0x371a;PLPCDU_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h +14100;0x3714;POSSIBLE_FILE_CORRUPTION;LOW;P1: Result code of TM packet parser. P2: Timestamp of possibly corrupt file as a unix timestamp.;mission/tmtc/PersistentTmStore.h +14200;0x3778;NO_VALID_SENSOR_TEMPERATURE;MEDIUM;No description;mission/controller/ThermalController.h +14201;0x3779;NO_HEALTHY_HEATER_AVAILABLE;MEDIUM;No description;mission/controller/ThermalController.h +14202;0x377a;SYRLINKS_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h +14203;0x377b;PLOC_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h +14204;0x377c;OBC_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h +14205;0x377d;HPA_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h +14206;0x377e;PLPCDU_OVERHEATING;HIGH;No description;mission/controller/ThermalController.h diff --git a/generators/bsp_q7s_objects.csv b/generators/bsp_q7s_objects.csv index 91e3a505..2bc03e8f 100644 --- a/generators/bsp_q7s_objects.csv +++ b/generators/bsp_q7s_objects.csv @@ -102,6 +102,7 @@ 0x53000008;PUS_SERVICE_8_FUNCTION_MGMT 0x53000009;PUS_SERVICE_9_TIME_MGMT 0x53000011;PUS_SERVICE_11_TC_SCHEDULER +0x53000015;PUS_SERVICE_15_TM_STORAGE 0x53000017;PUS_SERVICE_17_TEST 0x53000020;PUS_SERVICE_20_PARAMETERS 0x53000200;PUS_SERVICE_200_MODE_MGMT @@ -148,6 +149,11 @@ 0x73010002;PL_SUBSYSTEM 0x73010003;TCS_SUBSYSTEM 0x73010004;COM_SUBSYSTEM +0x73020001;MISC_TM_STORE +0x73020002;OK_TM_STORE +0x73020003;NOT_OK_TM_STORE +0x73020004;HK_TM_STORE +0x73030000;CFDP_TM_STORE 0x73500000;CCSDS_IP_CORE_BRIDGE 0x90000003;THERMAL_TEMP_INSERTER 0xFFFFFFFF;NO_OBJECT diff --git a/generators/bsp_q7s_returnvalues.csv b/generators/bsp_q7s_returnvalues.csv index 6f215a94..4abc2ff7 100644 --- a/generators/bsp_q7s_returnvalues.csv +++ b/generators/bsp_q7s_returnvalues.csv @@ -49,16 +49,17 @@ Full ID (hex); Name; Description; Unique ID; Subsytem Name; File Path 0x4fa4;HEATER_MainSwitchSetTimeout;No description;164;HEATER_HANDLER;mission/devices/HeaterHandler.h 0x4fa5;HEATER_CommandAlreadyWaiting;No description;165;HEATER_HANDLER;mission/devices/HeaterHandler.h 0x60a0;CCSDS_CommandNotImplemented;Received action message with unknown action id;160;CCSDS_HANDLER;mission/tmtc/CcsdsIpCoreHandler.h -0x6a01;ACSSAF_SafectrlMekfInputInvalid;No description;1;ACS_SAFE;mission/controller/acs/control/SafeCtrl.h -0x6b01;ACSPTG_PtgctrlMekfInputInvalid;No description;1;ACS_PTG;mission/controller/acs/control/PtgCtrl.h -0x6c01;ACSDTB_DetumbleNoSensordata;No description;1;ACS_DETUMBLE;mission/controller/acs/control/Detumble.h -0x6902;ACSKAL_KalmanUninitialized;No description;2;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6903;ACSKAL_KalmanNoGyrData;No description;3;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6904;ACSKAL_KalmanNoModelVectors;No description;4;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6905;ACSKAL_KalmanNoSusMgmStrData;No description;5;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6906;ACSKAL_KalmanCovarianceInversionFailed;No description;6;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6907;ACSKAL_KalmanInitialized;No description;7;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h -0x6908;ACSKAL_KalmanRunning;No description;8;ACS_KALMAN;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6b01;ACSSAF_SafectrlMekfInputInvalid;No description;1;ACS_SAFE;mission/controller/acs/control/SafeCtrl.h +0x6c01;ACSPTG_PtgctrlMekfInputInvalid;No description;1;ACS_PTG;mission/controller/acs/control/PtgCtrl.h +0x6d01;ACSDTB_DetumbleNoSensordata;No description;1;ACS_DETUMBLE;mission/controller/acs/control/Detumble.h +0x6a02;ACSMEKF_MekfUninitialized;No description;2;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a03;ACSMEKF_MekfNoGyrData;No description;3;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a04;ACSMEKF_MekfNoModelVectors;No description;4;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a05;ACSMEKF_MekfNoSusMgmStrData;No description;5;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a06;ACSMEKF_MekfCovarianceInversionFailed;No description;6;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a07;ACSMEKF_MekfInitialized;No description;7;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6a08;ACSMEKF_MekfRunning;No description;8;ACS_MEKF;mission/controller/acs/MultiplicativeKalmanFilter.h +0x6900;ACSCTRL_FileDeletionFailed;No description;0;ACS_CTRL;mission/controller/AcsController.h 0x4500;HSPI_OpeningFileFailed;No description;0;HAL_SPI;fsfw/src/fsfw_hal/linux/spi/SpiComIF.h 0x4501;HSPI_FullDuplexTransferFailed;No description;1;HAL_SPI;fsfw/src/fsfw_hal/linux/spi/SpiComIF.h 0x4502;HSPI_HalfDuplexTransferFailed;No description;2;HAL_SPI;fsfw/src/fsfw_hal/linux/spi/SpiComIF.h diff --git a/generators/events/translateEvents.cpp b/generators/events/translateEvents.cpp index e35a31ef..c979ff5e 100644 --- a/generators/events/translateEvents.cpp +++ b/generators/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** - * @brief Auto-generated event translation file. Contains 260 translations. + * @brief Auto-generated event translation file. Contains 263 translations. * @details - * Generated on: 2023-02-23 15:39:20 + * Generated on: 2023-02-24 16:57:00 */ #include "translateEvents.h" @@ -90,6 +90,8 @@ const char *CHANGE_OF_SETUP_PARAMETER_STRING = "CHANGE_OF_SETUP_PARAMETER"; const char *STORE_ERROR_STRING = "STORE_ERROR"; const char *MSG_QUEUE_ERROR_STRING = "MSG_QUEUE_ERROR"; const char *SERIALIZATION_ERROR_STRING = "SERIALIZATION_ERROR"; +const char *FILESTORE_ERROR_STRING = "FILESTORE_ERROR"; +const char *FILENAME_TOO_LARGE_ERROR_STRING = "FILENAME_TOO_LARGE_ERROR"; const char *SAFE_RATE_VIOLATION_STRING = "SAFE_RATE_VIOLATION"; const char *SAFE_RATE_RECOVERY_STRING = "SAFE_RATE_RECOVERY"; const char *MULTIPLE_RW_INVALID_STRING = "MULTIPLE_RW_INVALID"; @@ -253,6 +255,7 @@ const char *REBOOT_HW_STRING = "REBOOT_HW"; const char *NO_SD_CARD_ACTIVE_STRING = "NO_SD_CARD_ACTIVE"; const char *VERSION_INFO_STRING = "VERSION_INFO"; const char *CURRENT_IMAGE_INFO_STRING = "CURRENT_IMAGE_INFO"; +const char *POSSIBLE_FILE_CORRUPTION_STRING = "POSSIBLE_FILE_CORRUPTION"; const char *NO_VALID_SENSOR_TEMPERATURE_STRING = "NO_VALID_SENSOR_TEMPERATURE"; const char *NO_HEALTHY_HEATER_AVAILABLE_STRING = "NO_HEALTHY_HEATER_AVAILABLE"; const char *SYRLINKS_OVERHEATING_STRING = "SYRLINKS_OVERHEATING"; @@ -433,6 +436,10 @@ const char *translateEvents(Event event) { return MSG_QUEUE_ERROR_STRING; case (10802): return SERIALIZATION_ERROR_STRING; + case (10803): + return FILESTORE_ERROR_STRING; + case (10804): + return FILENAME_TOO_LARGE_ERROR_STRING; case (11200): return SAFE_RATE_VIOLATION_STRING; case (11201): @@ -760,18 +767,20 @@ const char *translateEvents(Event event) { case (14006): return CURRENT_IMAGE_INFO_STRING; case (14100): + return POSSIBLE_FILE_CORRUPTION_STRING; + case (14200): return NO_VALID_SENSOR_TEMPERATURE_STRING; - case (14101): + case (14201): return NO_HEALTHY_HEATER_AVAILABLE_STRING; - case (14102): + case (14202): return SYRLINKS_OVERHEATING_STRING; - case (14103): + case (14203): return PLOC_OVERHEATING_STRING; - case (14104): + case (14204): return OBC_OVERHEATING_STRING; - case (14105): + case (14205): return HPA_OVERHEATING_STRING; - case (14106): + case (14206): return PLPCDU_OVERHEATING_STRING; default: return "UNKNOWN_EVENT"; diff --git a/generators/objects/translateObjects.cpp b/generators/objects/translateObjects.cpp index 70bcf3ad..93c4fb0f 100644 --- a/generators/objects/translateObjects.cpp +++ b/generators/objects/translateObjects.cpp @@ -1,8 +1,8 @@ /** * @brief Auto-generated object translation file. * @details - * Contains 153 translations. - * Generated on: 2023-02-23 15:39:20 + * Contains 159 translations. + * Generated on: 2023-02-24 16:57:00 */ #include "translateObjects.h" @@ -110,6 +110,7 @@ const char *PUS_SERVICE_5_EVENT_REPORTING_STRING = "PUS_SERVICE_5_EVENT_REPORTIN const char *PUS_SERVICE_8_FUNCTION_MGMT_STRING = "PUS_SERVICE_8_FUNCTION_MGMT"; const char *PUS_SERVICE_9_TIME_MGMT_STRING = "PUS_SERVICE_9_TIME_MGMT"; const char *PUS_SERVICE_11_TC_SCHEDULER_STRING = "PUS_SERVICE_11_TC_SCHEDULER"; +const char *PUS_SERVICE_15_TM_STORAGE_STRING = "PUS_SERVICE_15_TM_STORAGE"; const char *PUS_SERVICE_17_TEST_STRING = "PUS_SERVICE_17_TEST"; const char *PUS_SERVICE_20_PARAMETERS_STRING = "PUS_SERVICE_20_PARAMETERS"; const char *PUS_SERVICE_200_MODE_MGMT_STRING = "PUS_SERVICE_200_MODE_MGMT"; @@ -156,6 +157,11 @@ const char *ACS_SUBSYSTEM_STRING = "ACS_SUBSYSTEM"; const char *PL_SUBSYSTEM_STRING = "PL_SUBSYSTEM"; const char *TCS_SUBSYSTEM_STRING = "TCS_SUBSYSTEM"; const char *COM_SUBSYSTEM_STRING = "COM_SUBSYSTEM"; +const char *MISC_TM_STORE_STRING = "MISC_TM_STORE"; +const char *OK_TM_STORE_STRING = "OK_TM_STORE"; +const char *NOT_OK_TM_STORE_STRING = "NOT_OK_TM_STORE"; +const char *HK_TM_STORE_STRING = "HK_TM_STORE"; +const char *CFDP_TM_STORE_STRING = "CFDP_TM_STORE"; const char *CCSDS_IP_CORE_BRIDGE_STRING = "CCSDS_IP_CORE_BRIDGE"; const char *THERMAL_TEMP_INSERTER_STRING = "THERMAL_TEMP_INSERTER"; const char *NO_OBJECT_STRING = "NO_OBJECT"; @@ -370,6 +376,8 @@ const char *translateObject(object_id_t object) { return PUS_SERVICE_9_TIME_MGMT_STRING; case 0x53000011: return PUS_SERVICE_11_TC_SCHEDULER_STRING; + case 0x53000015: + return PUS_SERVICE_15_TM_STORAGE_STRING; case 0x53000017: return PUS_SERVICE_17_TEST_STRING; case 0x53000020: @@ -462,6 +470,16 @@ const char *translateObject(object_id_t object) { return TCS_SUBSYSTEM_STRING; case 0x73010004: return COM_SUBSYSTEM_STRING; + case 0x73020001: + return MISC_TM_STORE_STRING; + case 0x73020002: + return OK_TM_STORE_STRING; + case 0x73020003: + return NOT_OK_TM_STORE_STRING; + case 0x73020004: + return HK_TM_STORE_STRING; + case 0x73030000: + return CFDP_TM_STORE_STRING; case 0x73500000: return CCSDS_IP_CORE_BRIDGE_STRING; case 0x90000003: diff --git a/linux/devices/GpsHyperionLinuxController.cpp b/linux/devices/GpsHyperionLinuxController.cpp index a6e0a1a3..3b396504 100644 --- a/linux/devices/GpsHyperionLinuxController.cpp +++ b/linux/devices/GpsHyperionLinuxController.cpp @@ -172,20 +172,10 @@ bool GpsHyperionLinuxController::readGpsDataFromGpsd() { return false; } oneShotSwitches.gpsReadFailedSwitch = true; - // did not event get mode, nothing to see. - if (MODE_SET != (MODE_SET & gps.set)) { - if (mode != MODE_OFF) { - if (maxTimeToReachFix.hasTimedOut() and oneShotSwitches.cantGetFixSwitch) { - sif::warning - << "GPSHyperionHandler::readGpsDataFromGpsd: No mode could be set in allowed " - << maxTimeToReachFix.timeout / 1000 << " seconds" << std::endl; - triggerEvent(GpsHyperion::CANT_GET_FIX, maxTimeToReachFix.timeout); - oneShotSwitches.cantGetFixSwitch = false; - } - // Mode is on, so do next read immediately - return true; - } - // GPS device is off anyway, so do other handling + ReturnValue_t result = handleGpsReadData(); + if (result == returnvalue::OK) { + return true; + } else { return false; } noModeSetCntr = 0; @@ -197,11 +187,26 @@ bool GpsHyperionLinuxController::readGpsDataFromGpsd() { "SHM read not implemented" << std::endl; } - handleGpsReadData(); return true; } ReturnValue_t GpsHyperionLinuxController::handleGpsReadData() { + bool modeIsSet = true; + if (MODE_SET != (MODE_SET & gps.set)) { + if (mode != MODE_OFF) { + if (maxTimeToReachFix.hasTimedOut() and oneShotSwitches.cantGetFixSwitch) { + sif::warning << "GpsHyperionLinuxController: No mode could be set in allowed " + << maxTimeToReachFix.timeout / 1000 << " seconds" << std::endl; + triggerEvent(GpsHyperion::CANT_GET_FIX, maxTimeToReachFix.timeout); + oneShotSwitches.cantGetFixSwitch = false; + } + modeIsSet = false; + } else { + // GPS device is off anyway, so do other handling + return returnvalue::FAILED; + } + } + PoolReadGuard pg(&gpsSet); if (pg.getReadResult() != returnvalue::OK) { #if FSFW_VERBOSE_LEVEL >= 1 @@ -211,66 +216,93 @@ ReturnValue_t GpsHyperionLinuxController::handleGpsReadData() { } bool validFix = false; - // 0: Not seen, 1: No fix, 2: 2D-Fix, 3: 3D-Fix - if (gps.fix.mode == 2 or gps.fix.mode == 3) { - validFix = true; - } - if (gpsSet.fixMode.value != gps.fix.mode) { - triggerEvent(GpsHyperion::GPS_FIX_CHANGE, gpsSet.fixMode.value, gps.fix.mode); - } - gpsSet.fixMode.value = gps.fix.mode; - if (gps.fix.mode == 0 or gps.fix.mode == 1) { - if (modeCommanded and maxTimeToReachFix.hasTimedOut()) { - // We are supposed to be on and functioning, but no fix was found - if (mode == MODE_ON or mode == MODE_NORMAL) { - mode = MODE_OFF; - } - modeCommanded = false; + if (modeIsSet) { + // 0: Not seen, 1: No fix, 2: 2D-Fix, 3: 3D-Fix + if (gps.fix.mode == 2 or gps.fix.mode == 3) { + validFix = true; + } + if (gpsSet.fixMode.value != gps.fix.mode) { + triggerEvent(GpsHyperion::GPS_FIX_CHANGE, gpsSet.fixMode.value, gps.fix.mode); + } + gpsSet.fixMode.value = gps.fix.mode; + if (gps.fix.mode == 0 or gps.fix.mode == 1) { + if (modeCommanded and maxTimeToReachFix.hasTimedOut()) { + // We are supposed to be on and functioning, but no fix was found + if (mode == MODE_ON or mode == MODE_NORMAL) { + mode = MODE_OFF; + } + modeCommanded = false; + } + } + } + gpsSet.fixMode.setValid(modeIsSet); + + // Only set on specific messages, so only set a valid flag to invalid + // if not set for more than a full message set (10 messages here) + if (SATELLITE_SET == (SATELLITE_SET & gps.set)) { + gpsSet.satInUse.value = gps.satellites_used; + gpsSet.satInView.value = gps.satellites_visible; + if (not gpsSet.satInUse.isValid()) { + gpsSet.satInUse.setValid(true); + gpsSet.satInView.setValid(true); + } + satNotSetCounter = 0; + } else { + satNotSetCounter++; + if (gpsSet.satInUse.isValid() and satNotSetCounter >= 10) { + gpsSet.satInUse.setValid(false); + gpsSet.satInView.setValid(false); } - gpsSet.setValidity(false, true); - } else if (gps.satellites_used > 0 && validFix && mode != MODE_OFF) { - gpsSet.setValidity(true, true); } - gpsSet.satInUse.value = gps.satellites_used; - gpsSet.satInView.value = gps.satellites_visible; - + // LATLON is set for every message, no need for a counter bool latValid = false; - if (std::isfinite(gps.fix.latitude)) { - // Negative latitude -> South direction - gpsSet.latitude.value = gps.fix.latitude; - if (gps.fix.mode >= 2) { - latValid = true; + bool longValid = false; + if (LATLON_SET == (LATLON_SET & gps.set)) { + if (std::isfinite(gps.fix.latitude)) { + // Negative latitude -> South direction + gpsSet.latitude.value = gps.fix.latitude; + // As specified in gps.h: Only valid if mode >= 2 + if (gps.fix.mode >= 2) { + latValid = true; + } + } + + if (std::isfinite(gps.fix.longitude)) { + // Negative longitude -> West direction + gpsSet.longitude.value = gps.fix.longitude; + // As specified in gps.h: Only valid if mode >= 2 + if (gps.fix.mode >= 2) { + longValid = true; + } } } gpsSet.latitude.setValid(latValid); + gpsSet.longitude.setValid(longValid); - bool longValid = false; - if (std::isfinite(gps.fix.longitude)) { - // Negative longitude -> West direction - gpsSet.longitude.value = gps.fix.longitude; - if (gps.fix.mode >= 2) { - longValid = true; - } - } - gpsSet.latitude.setValid(longValid); - + // ALTITUDE is set for every message, no need for a counter bool altitudeValid = false; - if (std::isfinite(gps.fix.altitude)) { + if (ALTITUDE_SET == (ALTITUDE_SET & gps.set) && std::isfinite(gps.fix.altitude)) { gpsSet.altitude.value = gps.fix.altitude; + // As specified in gps.h: Only valid if mode == 3 if (gps.fix.mode == 3) { altitudeValid = true; } } gpsSet.altitude.setValid(altitudeValid); - if (std::isfinite(gps.fix.speed)) { + // SPEED is set for every message, no need for a counter + bool speedValid = false; + if (SPEED_SET == (SPEED_SET & gps.set) && std::isfinite(gps.fix.speed)) { gpsSet.speed.value = gps.fix.speed; - } else { - gpsSet.speed.setValid(false); + speedValid = true; } + gpsSet.speed.setValid(speedValid); + // TIME is set for every message, no need for a counter + bool timeValid = false; if (TIME_SET == (TIME_SET & gps.set)) { + timeValid = true; timeval time = {}; #if LIBGPS_VERSION_MINOR <= 17 gpsSet.unixSeconds.value = std::floor(gps.fix.time); @@ -294,15 +326,14 @@ ReturnValue_t GpsHyperionLinuxController::handleGpsReadData() { gpsSet.hours = timeOfDay.hour; gpsSet.minutes = timeOfDay.minute; gpsSet.seconds = timeOfDay.second; - } else { - gpsSet.unixSeconds.setValid(false); - gpsSet.year.setValid(false); - gpsSet.month.setValid(false); - gpsSet.day.setValid(false); - gpsSet.hours.setValid(false); - gpsSet.minutes.setValid(false); - gpsSet.seconds.setValid(false); } + gpsSet.unixSeconds.setValid(timeValid); + gpsSet.year.setValid(timeValid); + gpsSet.month.setValid(timeValid); + gpsSet.day.setValid(timeValid); + gpsSet.hours.setValid(timeValid); + gpsSet.minutes.setValid(timeValid); + gpsSet.seconds.setValid(timeValid); if (debugHyperionGps) { sif::info << "-- Hyperion GPS Data --" << std::endl; diff --git a/linux/devices/GpsHyperionLinuxController.h b/linux/devices/GpsHyperionLinuxController.h index 1e97bc29..df08d8a4 100644 --- a/linux/devices/GpsHyperionLinuxController.h +++ b/linux/devices/GpsHyperionLinuxController.h @@ -61,6 +61,8 @@ class GpsHyperionLinuxController : public ExtendedControllerBase { Countdown maxTimeToReachFix = Countdown(MAX_SECONDS_TO_REACH_FIX * 1000); bool modeCommanded = false; bool timeInit = false; + uint8_t satNotSetCounter = 0; + #if OBSW_THREAD_TRACING == 1 uint32_t opCounter = 0; #endif diff --git a/linux/devices/ploc/PlocSupervisorHandler.cpp b/linux/devices/ploc/PlocSupervisorHandler.cpp index 086ef661..06a4cf07 100644 --- a/linux/devices/ploc/PlocSupervisorHandler.cpp +++ b/linux/devices/ploc/PlocSupervisorHandler.cpp @@ -1742,18 +1742,21 @@ ReturnValue_t PlocSupervisorHandler::createMramDumpFile() { std::string filename = "mram-dump--" + timeStamp + ".bin"; #ifdef XIPHOS_Q7S - std::string currentMountPrefix = sdcMan->getCurrentMountPrefix(); + const char* currentMountPrefix = sdcMan->getCurrentMountPrefix(); #else - std::string currentMountPrefix("/mnt/sd0"); + const char* currentMountPrefix = "/mnt/sd0"; #endif /* BOARD_TE0720 == 0 */ + if (currentMountPrefix == nullptr) { + return returnvalue::FAILED; + } // Check if path to PLOC directory exists - if (not std::filesystem::exists(std::string(currentMountPrefix + "/" + supervisorFilePath))) { + if (not std::filesystem::exists(std::string(currentMountPrefix) + "/" + supervisorFilePath)) { sif::warning << "PlocSupervisorHandler::createMramDumpFile: Supervisor path does not exist" << std::endl; return result::PATH_DOES_NOT_EXIST; } - activeMramFile = currentMountPrefix + "/" + supervisorFilePath + "/" + filename; + activeMramFile = std::string(currentMountPrefix) + "/" + supervisorFilePath + "/" + filename; // Create new file std::ofstream file(activeMramFile, std::ios_base::out); file.close(); diff --git a/linux/fsfwconfig/FSFWConfig.h.in b/linux/fsfwconfig/FSFWConfig.h.in index 387ef724..677cf0af 100644 --- a/linux/fsfwconfig/FSFWConfig.h.in +++ b/linux/fsfwconfig/FSFWConfig.h.in @@ -42,7 +42,7 @@ //! When using the newlib nano library, C99 support for stdio facilities //! will not be provided. This define should be set to 1 if this is the case. -#define FSFW_NO_C99_IO 1 +#define FSFW_NO_C99_IO 0 //! Specify whether a special mode store is used for Subsystem components. #define FSFW_USE_MODESTORE 0 diff --git a/linux/fsfwconfig/events/translateEvents.cpp b/linux/fsfwconfig/events/translateEvents.cpp index e35a31ef..c979ff5e 100644 --- a/linux/fsfwconfig/events/translateEvents.cpp +++ b/linux/fsfwconfig/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** - * @brief Auto-generated event translation file. Contains 260 translations. + * @brief Auto-generated event translation file. Contains 263 translations. * @details - * Generated on: 2023-02-23 15:39:20 + * Generated on: 2023-02-24 16:57:00 */ #include "translateEvents.h" @@ -90,6 +90,8 @@ const char *CHANGE_OF_SETUP_PARAMETER_STRING = "CHANGE_OF_SETUP_PARAMETER"; const char *STORE_ERROR_STRING = "STORE_ERROR"; const char *MSG_QUEUE_ERROR_STRING = "MSG_QUEUE_ERROR"; const char *SERIALIZATION_ERROR_STRING = "SERIALIZATION_ERROR"; +const char *FILESTORE_ERROR_STRING = "FILESTORE_ERROR"; +const char *FILENAME_TOO_LARGE_ERROR_STRING = "FILENAME_TOO_LARGE_ERROR"; const char *SAFE_RATE_VIOLATION_STRING = "SAFE_RATE_VIOLATION"; const char *SAFE_RATE_RECOVERY_STRING = "SAFE_RATE_RECOVERY"; const char *MULTIPLE_RW_INVALID_STRING = "MULTIPLE_RW_INVALID"; @@ -253,6 +255,7 @@ const char *REBOOT_HW_STRING = "REBOOT_HW"; const char *NO_SD_CARD_ACTIVE_STRING = "NO_SD_CARD_ACTIVE"; const char *VERSION_INFO_STRING = "VERSION_INFO"; const char *CURRENT_IMAGE_INFO_STRING = "CURRENT_IMAGE_INFO"; +const char *POSSIBLE_FILE_CORRUPTION_STRING = "POSSIBLE_FILE_CORRUPTION"; const char *NO_VALID_SENSOR_TEMPERATURE_STRING = "NO_VALID_SENSOR_TEMPERATURE"; const char *NO_HEALTHY_HEATER_AVAILABLE_STRING = "NO_HEALTHY_HEATER_AVAILABLE"; const char *SYRLINKS_OVERHEATING_STRING = "SYRLINKS_OVERHEATING"; @@ -433,6 +436,10 @@ const char *translateEvents(Event event) { return MSG_QUEUE_ERROR_STRING; case (10802): return SERIALIZATION_ERROR_STRING; + case (10803): + return FILESTORE_ERROR_STRING; + case (10804): + return FILENAME_TOO_LARGE_ERROR_STRING; case (11200): return SAFE_RATE_VIOLATION_STRING; case (11201): @@ -760,18 +767,20 @@ const char *translateEvents(Event event) { case (14006): return CURRENT_IMAGE_INFO_STRING; case (14100): + return POSSIBLE_FILE_CORRUPTION_STRING; + case (14200): return NO_VALID_SENSOR_TEMPERATURE_STRING; - case (14101): + case (14201): return NO_HEALTHY_HEATER_AVAILABLE_STRING; - case (14102): + case (14202): return SYRLINKS_OVERHEATING_STRING; - case (14103): + case (14203): return PLOC_OVERHEATING_STRING; - case (14104): + case (14204): return OBC_OVERHEATING_STRING; - case (14105): + case (14205): return HPA_OVERHEATING_STRING; - case (14106): + case (14206): return PLPCDU_OVERHEATING_STRING; default: return "UNKNOWN_EVENT"; diff --git a/linux/fsfwconfig/objects/translateObjects.cpp b/linux/fsfwconfig/objects/translateObjects.cpp index 70bcf3ad..93c4fb0f 100644 --- a/linux/fsfwconfig/objects/translateObjects.cpp +++ b/linux/fsfwconfig/objects/translateObjects.cpp @@ -1,8 +1,8 @@ /** * @brief Auto-generated object translation file. * @details - * Contains 153 translations. - * Generated on: 2023-02-23 15:39:20 + * Contains 159 translations. + * Generated on: 2023-02-24 16:57:00 */ #include "translateObjects.h" @@ -110,6 +110,7 @@ const char *PUS_SERVICE_5_EVENT_REPORTING_STRING = "PUS_SERVICE_5_EVENT_REPORTIN const char *PUS_SERVICE_8_FUNCTION_MGMT_STRING = "PUS_SERVICE_8_FUNCTION_MGMT"; const char *PUS_SERVICE_9_TIME_MGMT_STRING = "PUS_SERVICE_9_TIME_MGMT"; const char *PUS_SERVICE_11_TC_SCHEDULER_STRING = "PUS_SERVICE_11_TC_SCHEDULER"; +const char *PUS_SERVICE_15_TM_STORAGE_STRING = "PUS_SERVICE_15_TM_STORAGE"; const char *PUS_SERVICE_17_TEST_STRING = "PUS_SERVICE_17_TEST"; const char *PUS_SERVICE_20_PARAMETERS_STRING = "PUS_SERVICE_20_PARAMETERS"; const char *PUS_SERVICE_200_MODE_MGMT_STRING = "PUS_SERVICE_200_MODE_MGMT"; @@ -156,6 +157,11 @@ const char *ACS_SUBSYSTEM_STRING = "ACS_SUBSYSTEM"; const char *PL_SUBSYSTEM_STRING = "PL_SUBSYSTEM"; const char *TCS_SUBSYSTEM_STRING = "TCS_SUBSYSTEM"; const char *COM_SUBSYSTEM_STRING = "COM_SUBSYSTEM"; +const char *MISC_TM_STORE_STRING = "MISC_TM_STORE"; +const char *OK_TM_STORE_STRING = "OK_TM_STORE"; +const char *NOT_OK_TM_STORE_STRING = "NOT_OK_TM_STORE"; +const char *HK_TM_STORE_STRING = "HK_TM_STORE"; +const char *CFDP_TM_STORE_STRING = "CFDP_TM_STORE"; const char *CCSDS_IP_CORE_BRIDGE_STRING = "CCSDS_IP_CORE_BRIDGE"; const char *THERMAL_TEMP_INSERTER_STRING = "THERMAL_TEMP_INSERTER"; const char *NO_OBJECT_STRING = "NO_OBJECT"; @@ -370,6 +376,8 @@ const char *translateObject(object_id_t object) { return PUS_SERVICE_9_TIME_MGMT_STRING; case 0x53000011: return PUS_SERVICE_11_TC_SCHEDULER_STRING; + case 0x53000015: + return PUS_SERVICE_15_TM_STORAGE_STRING; case 0x53000017: return PUS_SERVICE_17_TEST_STRING; case 0x53000020: @@ -462,6 +470,16 @@ const char *translateObject(object_id_t object) { return TCS_SUBSYSTEM_STRING; case 0x73010004: return COM_SUBSYSTEM_STRING; + case 0x73020001: + return MISC_TM_STORE_STRING; + case 0x73020002: + return OK_TM_STORE_STRING; + case 0x73020003: + return NOT_OK_TM_STORE_STRING; + case 0x73020004: + return HK_TM_STORE_STRING; + case 0x73030000: + return CFDP_TM_STORE_STRING; case 0x73500000: return CCSDS_IP_CORE_BRIDGE_STRING; case 0x90000003: diff --git a/mission/controller/AcsController.cpp b/mission/controller/AcsController.cpp index 6b821a08..f3269285 100644 --- a/mission/controller/AcsController.cpp +++ b/mission/controller/AcsController.cpp @@ -1,9 +1,8 @@ #include "AcsController.h" #include - -#include "mission/acsDefs.h" -#include "mission/config/torquer.h" +#include +#include AcsController::AcsController(object_id_t objectId) : ExtendedControllerBase(objectId), @@ -46,6 +45,26 @@ ReturnValue_t AcsController::handleCommandMessage(CommandMessage *message) { return result; } +ReturnValue_t AcsController::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, + const uint8_t *data, size_t size) { + switch (actionId) { + case SOLAR_ARRAY_DEPLOYMENT_SUCCESSFUL: { + ReturnValue_t result = guidance.solarArrayDeploymentComplete(); + if (result == returnvalue::FAILED) { + return FILE_DELETION_FAILED; + } + return HasActionsIF::EXECUTION_FINISHED; + } + case RESET_MEKF: { + navigation.resetMekf(&mekfData); + return HasActionsIF::EXECUTION_FINISHED; + } + default: { + return HasActionsIF::INVALID_ACTION_ID; + } + } +} + MessageQueueId_t AcsController::getCommandQueue() const { return commandQueue->getId(); } ReturnValue_t AcsController::getParameter(uint8_t domainId, uint8_t parameterId, @@ -60,6 +79,25 @@ void AcsController::performControlOperation() { #if OBSW_THREAD_TRACING == 1 trace::threadTrace(opCounter, "ACS & TCS PST"); #endif + { + PoolReadGuard pg(&mgmDataRaw); + if (pg.getReadResult() == returnvalue::OK) { + copyMgmData(); + } + } + { + PoolReadGuard pg(&susDataRaw); + if (pg.getReadResult() == returnvalue::OK) { + copySusData(); + } + } + { + PoolReadGuard pg(&gyrDataRaw); + if (pg.getReadResult() == returnvalue::OK) { + copyGyrData(); + } + } + switch (internalState) { case InternalState::STARTUP: { initialCountdown.resetTimer(); @@ -95,25 +133,6 @@ void AcsController::performControlOperation() { default: break; } - - { - PoolReadGuard pg(&mgmDataRaw); - if (pg.getReadResult() == returnvalue::OK) { - copyMgmData(); - } - } - { - PoolReadGuard pg(&susDataRaw); - if (pg.getReadResult() == returnvalue::OK) { - copySusData(); - } - } - { - PoolReadGuard pg(&gyrDataRaw); - if (pg.getReadResult() == returnvalue::OK) { - copyGyrData(); - } - } } void AcsController::performSafe() { @@ -124,8 +143,8 @@ void AcsController::performSafe() { &gyrDataProcessed, &gpsDataProcessed, &acsParameters); ReturnValue_t result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed, &susDataProcessed, &mekfData); - if (result != MultiplicativeKalmanFilter::KALMAN_RUNNING && - result != MultiplicativeKalmanFilter::KALMAN_INITIALIZED) { + if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && + result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) { if (not mekfInvalidFlag) { triggerEvent(acs::MEKF_INVALID_INFO); mekfInvalidFlag = true; @@ -139,7 +158,7 @@ void AcsController::performSafe() { // if MEKF is working double magMomMtq[3] = {0, 0, 0}, errAng = 0.0; bool magMomMtqValid = false; - if (result == MultiplicativeKalmanFilter::KALMAN_RUNNING) { + if (result == MultiplicativeKalmanFilter::MEKF_RUNNING) { safeCtrl.safeMekf(now, mekfData.quatMekf.value, mekfData.quatMekf.isValid(), mgmDataProcessed.magIgrfModel.value, mgmDataProcessed.magIgrfModel.isValid(), susDataProcessed.sunIjkModel.value, susDataProcessed.isValid(), @@ -189,8 +208,8 @@ void AcsController::performDetumble() { &gyrDataProcessed, &gpsDataProcessed, &acsParameters); ReturnValue_t result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed, &susDataProcessed, &mekfData); - if (result != MultiplicativeKalmanFilter::KALMAN_RUNNING && - result != MultiplicativeKalmanFilter::KALMAN_INITIALIZED) { + if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && + result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) { if (not mekfInvalidFlag) { triggerEvent(acs::MEKF_INVALID_INFO); mekfInvalidFlag = true; @@ -236,8 +255,8 @@ void AcsController::performPointingCtrl() { &gyrDataProcessed, &gpsDataProcessed, &acsParameters); ReturnValue_t result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed, &susDataProcessed, &mekfData); - if (result != MultiplicativeKalmanFilter::KALMAN_RUNNING && - result != MultiplicativeKalmanFilter::KALMAN_INITIALIZED) { + if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && + result != MultiplicativeKalmanFilter::MEKF_INITIALIZED) { if (not mekfInvalidFlag) { triggerEvent(acs::MEKF_INVALID_INFO); mekfInvalidFlag = true; @@ -246,9 +265,10 @@ void AcsController::performPointingCtrl() { triggerEvent(acs::MEKF_INVALID_MODE_VIOLATION); } mekfInvalidCounter++; - commandActuators(0, 0, 0, acsParameters.magnetorquesParameter.torqueDuration, cmdSpeedRws[0], - cmdSpeedRws[1], cmdSpeedRws[2], cmdSpeedRws[3], - acsParameters.rwHandlingParameters.rampTime); + // commandActuators(0, 0, 0, acsParameters.magnetorquesParameter.torqueDuration, + // cmdSpeedRws[0], + // cmdSpeedRws[1], cmdSpeedRws[2], cmdSpeedRws[3], + // acsParameters.rwHandlingParameters.rampTime); return; } else { mekfInvalidFlag = false; @@ -395,7 +415,7 @@ void AcsController::performPointingCtrl() { sensorValues.rw4Set.currSpeed.value, torqueRwsScaled, cmdSpeedRws); actuatorCmd.cmdDipolMtq(mgtDpDes, cmdDipolMtqs); - updateCtrlValData(targetQuat, errorQuat, errorAngle); + updateCtrlValData(targetQuat, errorQuat, errorAngle, targetSatRotRate); updateActuatorCmdData(rwTrqNs, cmdSpeedRws, cmdDipolMtqs); // commandActuators(cmdDipolMtqs[0], cmdDipolMtqs[1], cmdDipolMtqs[2], // acsParameters.magnetorquesParameter.torqueDuration, cmdSpeedRws[0], @@ -457,16 +477,20 @@ void AcsController::updateCtrlValData(double errAng) { ctrlValData.errQuat.setValid(false); ctrlValData.errAng.value = errAng; ctrlValData.errAng.setValid(true); + std::memcpy(ctrlValData.tgtRotRate.value, ZERO_VEC, 3 * sizeof(double)); + ctrlValData.tgtRotRate.setValid(false); ctrlValData.setValidity(true, false); } } -void AcsController::updateCtrlValData(const double *tgtQuat, const double *errQuat, double errAng) { +void AcsController::updateCtrlValData(const double *tgtQuat, const double *errQuat, double errAng, + const double *tgtRotRate) { PoolReadGuard pg(&ctrlValData); if (pg.getReadResult() == returnvalue::OK) { std::memcpy(ctrlValData.tgtQuat.value, tgtQuat, 4 * sizeof(double)); std::memcpy(ctrlValData.errQuat.value, errQuat, 4 * sizeof(double)); ctrlValData.errAng.value = errAng; + std::memcpy(ctrlValData.tgtRotRate.value, tgtRotRate, 3 * sizeof(double)); ctrlValData.setValidity(true, true); } } @@ -477,6 +501,7 @@ void AcsController::disableCtrlValData() { std::memcpy(ctrlValData.tgtQuat.value, UNIT_QUAT, 4 * sizeof(double)); std::memcpy(ctrlValData.errQuat.value, UNIT_QUAT, 4 * sizeof(double)); ctrlValData.errAng.value = 0; + std::memcpy(ctrlValData.tgtRotRate.value, ZERO_VEC, 3 * sizeof(double)); ctrlValData.setValidity(false, true); } } diff --git a/mission/controller/AcsController.h b/mission/controller/AcsController.h index 1b57a32a..6d31ae49 100644 --- a/mission/controller/AcsController.h +++ b/mission/controller/AcsController.h @@ -1,12 +1,17 @@ #ifndef MISSION_CONTROLLER_ACSCONTROLLER_H_ #define MISSION_CONTROLLER_ACSCONTROLLER_H_ +#include #include #include #include #include +#include +#include +#include #include #include +#include #include "acs/ActuatorCmd.h" #include "acs/Guidance.h" @@ -17,11 +22,6 @@ #include "acs/control/PtgCtrl.h" #include "acs/control/SafeCtrl.h" #include "controllerdefinitions/AcsCtrlDefinitions.h" -#include "eive/objects.h" -#include "fsfw_hal/devicehandlers/MgmLIS3MDLHandler.h" -#include "fsfw_hal/devicehandlers/MgmRM3100Handler.h" -#include "mission/devices/devicedefinitions/SusDefinitions.h" -#include "mission/trace.h" class AcsController : public ExtendedControllerBase, public ReceivesParameterMessagesIF { public: @@ -41,6 +41,7 @@ class AcsController : public ExtendedControllerBase, public ReceivesParameterMes private: static constexpr double UNIT_QUAT[4] = {0, 0, 0, 1}; + static constexpr double ZERO_VEC[3] = {0, 0, 0}; static constexpr double RW_OFF_TORQUE[4] = {0.0, 0.0, 0.0, 0.0}; static constexpr int32_t RW_OFF_SPEED[4] = {0, 0, 0, 0}; @@ -58,7 +59,7 @@ class AcsController : public ExtendedControllerBase, public ReceivesParameterMes uint8_t detumbleCounter = 0; uint8_t multipleRwUnavailableCounter = 0; - bool mekfInvalidFlag = true; + bool mekfInvalidFlag = false; uint8_t mekfInvalidCounter = 0; int32_t cmdSpeedRws[4] = {0, 0, 0, 0}; int16_t cmdDipolMtqs[3] = {0, 0, 0}; @@ -68,13 +69,23 @@ class AcsController : public ExtendedControllerBase, public ReceivesParameterMes #endif enum class InternalState { STARTUP, INITIAL_DELAY, READY }; - InternalState internalState = InternalState::STARTUP; + /** Device command IDs */ + static const DeviceCommandId_t SOLAR_ARRAY_DEPLOYMENT_SUCCESSFUL = 0x0; + static const DeviceCommandId_t RESET_MEKF = 0x1; + + static const uint8_t INTERFACE_ID = CLASS_ID::ACS_CTRL; + static constexpr ReturnValue_t FILE_DELETION_FAILED = MAKE_RETURN_CODE(0); + ReturnValue_t initialize() override; ReturnValue_t handleCommandMessage(CommandMessage* message) override; void performControlOperation() override; + /* HasActionsIF overrides */ + ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, + const uint8_t* data, size_t size) override; + ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, LocalDataPoolManager& poolManager) override; LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override; @@ -92,7 +103,8 @@ class AcsController : public ExtendedControllerBase, public ReceivesParameterMes void updateActuatorCmdData(const double* rwTargetTorque, const int32_t* rwTargetSpeed, const int16_t* mtqTargetDipole); void updateCtrlValData(double errAng); - void updateCtrlValData(const double* tgtQuat, const double* errQuat, double errAng); + void updateCtrlValData(const double* tgtQuat, const double* errQuat, double errAng, + const double* tgtRotRate); void disableCtrlValData(); /* ACS Sensor Values */ diff --git a/mission/controller/acs/AcsParameters.h b/mission/controller/acs/AcsParameters.h index f1b538c8..11de10a3 100644 --- a/mission/controller/acs/AcsParameters.h +++ b/mission/controller/acs/AcsParameters.h @@ -1,8 +1,3 @@ -/******************************* - * EIVE Flight Software Framework (FSFW) - * (c) 2022 IRS, Uni Stuttgart - *******************************/ - #ifndef ACSPARAMETERS_H_ #define ACSPARAMETERS_H_ diff --git a/mission/controller/acs/Guidance.cpp b/mission/controller/acs/Guidance.cpp index 031cd384..09f0be20 100644 --- a/mission/controller/acs/Guidance.cpp +++ b/mission/controller/acs/Guidance.cpp @@ -551,3 +551,19 @@ void Guidance::getTargetParamsSafe(double sunTargetSafe[3], double satRateSafe[3 std::memcpy(satRateSafe, acsParameters.safeModeControllerParameters.satRateRef, 3 * sizeof(double)); } + +ReturnValue_t Guidance::solarArrayDeploymentComplete() { + if (std::filesystem::exists(SD_0_SKEWED_PTG_FILE)) { + std::remove(SD_0_SKEWED_PTG_FILE); + if (std::filesystem::exists(SD_0_SKEWED_PTG_FILE)) { + return returnvalue::FAILED; + } + } + if (std::filesystem::exists(SD_1_SKEWED_PTG_FILE)) { + std::remove(SD_1_SKEWED_PTG_FILE); + if (std::filesystem::exists(SD_1_SKEWED_PTG_FILE)) { + return returnvalue::FAILED; + } + } + return returnvalue::OK; +} diff --git a/mission/controller/acs/Guidance.h b/mission/controller/acs/Guidance.h index 65c9aa12..da9d429b 100644 --- a/mission/controller/acs/Guidance.h +++ b/mission/controller/acs/Guidance.h @@ -13,6 +13,7 @@ class Guidance { virtual ~Guidance(); void getTargetParamsSafe(double sunTargetSafe[3], double satRateRef[3]); + ReturnValue_t solarArrayDeploymentComplete(); // Function to get the target quaternion and refence rotation rate from gps position and // position of the ground station diff --git a/mission/controller/acs/MultiplicativeKalmanFilter.cpp b/mission/controller/acs/MultiplicativeKalmanFilter.cpp index 5bae4624..a700c6a6 100644 --- a/mission/controller/acs/MultiplicativeKalmanFilter.cpp +++ b/mission/controller/acs/MultiplicativeKalmanFilter.cpp @@ -189,12 +189,12 @@ ReturnValue_t MultiplicativeKalmanFilter::init( initialCovarianceMatrix[5][4] = initGyroCov[2][1]; initialCovarianceMatrix[5][5] = initGyroCov[2][2]; updateDataSetWithoutData(mekfData, MekfStatus::INITIALIZED); - return KALMAN_INITIALIZED; + return MEKF_INITIALIZED; } else { // no initialisation possible, no valid measurements validInit = false; updateDataSetWithoutData(mekfData, MekfStatus::UNINITIALIZED); - return KALMAN_UNINITIALIZED; + return MEKF_UNINITIALIZED; } } @@ -211,12 +211,12 @@ ReturnValue_t MultiplicativeKalmanFilter::mekfEst(const double *quaternionSTR, c int MDF = 0; // Matrix Dimension Factor if (!validGYRs_) { updateDataSetWithoutData(mekfData, MekfStatus::NO_GYR_DATA); - return KALMAN_NO_GYR_DATA; + return MEKF_NO_GYR_DATA; } // Check for Model Calculations else if (!validSSModel || !validMagModel) { updateDataSetWithoutData(mekfData, MekfStatus::NO_MODEL_VECTORS); - return KALMAN_NO_MODEL_VECTORS; + return MEKF_NO_MODEL_VECTORS; } // Check Measurements available from SS, MAG, STR if (validSS && validMagField_ && validSTR_) { @@ -854,7 +854,7 @@ ReturnValue_t MultiplicativeKalmanFilter::mekfEst(const double *quaternionSTR, c int inversionFailed = MathOperations::inverseMatrix(*residualCov, *invResidualCov, MDF); if (inversionFailed) { updateDataSetWithoutData(mekfData, MekfStatus::COVARIANCE_INVERSION_FAILED); - return KALMAN_COVARIANCE_INVERSION_FAILED; // RETURN VALUE ? -- Like: Kalman Inversion Failed + return MEKF_COVARIANCE_INVERSION_FAILED; // RETURN VALUE ? -- Like: Kalman Inversion Failed } // [K = P * H' / (H * P * H' + R)] @@ -1085,16 +1085,17 @@ ReturnValue_t MultiplicativeKalmanFilter::mekfEst(const double *quaternionSTR, c MatrixOperations::add(*cov0, *cov1, *initialCovarianceMatrix, 6, 6); updateDataSet(mekfData, MekfStatus::RUNNING, quatBJ, rotRateEst); - return KALMAN_RUNNING; + return MEKF_RUNNING; } -void MultiplicativeKalmanFilter::reset(acsctrl::MekfData *mekfData) { +ReturnValue_t MultiplicativeKalmanFilter::reset(acsctrl::MekfData *mekfData) { double resetQuaternion[4] = {0, 0, 0, 1}; double resetCovarianceMatrix[6][6] = {{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}}; std::memcpy(initialQuaternion, resetQuaternion, 4 * sizeof(double)); std::memcpy(initialCovarianceMatrix, resetCovarianceMatrix, 6 * 6 * sizeof(double)); updateDataSetWithoutData(mekfData, MekfStatus::UNINITIALIZED); + return MEKF_UNINITIALIZED; } void MultiplicativeKalmanFilter::updateDataSetWithoutData(acsctrl::MekfData *mekfData, diff --git a/mission/controller/acs/MultiplicativeKalmanFilter.h b/mission/controller/acs/MultiplicativeKalmanFilter.h index dd02cf9f..47e1f807 100644 --- a/mission/controller/acs/MultiplicativeKalmanFilter.h +++ b/mission/controller/acs/MultiplicativeKalmanFilter.h @@ -1,17 +1,3 @@ -/* - * MultiplicativeKalmanFilter.h - * - * Created on: 4 Feb 2022 - * Author: Robin Marquardt - * - * @brief: This class handles the calculation of an estimated quaternion and the gyro bias by - * means of the spacecraft attitude sensors - * - * @note: A description of the used algorithms can be found in the bachelor thesis of Robin - * Marquardt - * https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_Studenten/Marquardt_Robin&openfile=500811 - */ - #ifndef MULTIPLICATIVEKALMANFILTER_H_ #define MULTIPLICATIVEKALMANFILTER_H_ @@ -22,6 +8,13 @@ #include "eive/resultClassIds.h" class MultiplicativeKalmanFilter { + /* @brief: This class handles the calculation of an estimated quaternion and the gyro bias by + * means of the spacecraft attitude sensors + * + * @note: A description of the used algorithms can be found in the bachelor thesis of Robin + * Marquardt + * https://eive-cloud.irs.uni-stuttgart.de/index.php/apps/files/?dir=/EIVE_Studenten/Marquardt_Robin&openfile=500811 + */ public: /* @brief: Constructor * @param: acsParameters_ Pointer to object which defines the ACS configuration parameters @@ -29,7 +22,7 @@ class MultiplicativeKalmanFilter { MultiplicativeKalmanFilter(AcsParameters *acsParameters_); virtual ~MultiplicativeKalmanFilter(); - void reset(acsctrl::MekfData *mekfData); + ReturnValue_t reset(acsctrl::MekfData *mekfData); /* @brief: init() - This function initializes the Kalman Filter and will provide the first * quaternion through the QUEST algorithm @@ -74,15 +67,15 @@ class MultiplicativeKalmanFilter { }; // resetting Mekf - static constexpr uint8_t IF_KAL_ID = CLASS_ID::ACS_KALMAN; - static constexpr ReturnValue_t KALMAN_UNINITIALIZED = returnvalue::makeCode(IF_KAL_ID, 2); - static constexpr ReturnValue_t KALMAN_NO_GYR_DATA = returnvalue::makeCode(IF_KAL_ID, 3); - static constexpr ReturnValue_t KALMAN_NO_MODEL_VECTORS = returnvalue::makeCode(IF_KAL_ID, 4); - static constexpr ReturnValue_t KALMAN_NO_SUS_MGM_STR_DATA = returnvalue::makeCode(IF_KAL_ID, 5); - static constexpr ReturnValue_t KALMAN_COVARIANCE_INVERSION_FAILED = - returnvalue::makeCode(IF_KAL_ID, 6); - static constexpr ReturnValue_t KALMAN_INITIALIZED = returnvalue::makeCode(IF_KAL_ID, 7); - static constexpr ReturnValue_t KALMAN_RUNNING = returnvalue::makeCode(IF_KAL_ID, 8); + static constexpr uint8_t IF_MEKF_ID = CLASS_ID::ACS_MEKF; + static constexpr ReturnValue_t MEKF_UNINITIALIZED = returnvalue::makeCode(IF_MEKF_ID, 2); + static constexpr ReturnValue_t MEKF_NO_GYR_DATA = returnvalue::makeCode(IF_MEKF_ID, 3); + static constexpr ReturnValue_t MEKF_NO_MODEL_VECTORS = returnvalue::makeCode(IF_MEKF_ID, 4); + static constexpr ReturnValue_t MEKF_NO_SUS_MGM_STR_DATA = returnvalue::makeCode(IF_MEKF_ID, 5); + static constexpr ReturnValue_t MEKF_COVARIANCE_INVERSION_FAILED = + returnvalue::makeCode(IF_MEKF_ID, 6); + static constexpr ReturnValue_t MEKF_INITIALIZED = returnvalue::makeCode(IF_MEKF_ID, 7); + static constexpr ReturnValue_t MEKF_RUNNING = returnvalue::makeCode(IF_MEKF_ID, 8); private: /*Parameters*/ diff --git a/mission/controller/acs/Navigation.cpp b/mission/controller/acs/Navigation.cpp index c6310302..03446609 100644 --- a/mission/controller/acs/Navigation.cpp +++ b/mission/controller/acs/Navigation.cpp @@ -25,26 +25,25 @@ ReturnValue_t Navigation::useMekf(ACS::SensorValues *sensorValues, sensorValues->strSet.caliQy.isValid() && sensorValues->strSet.caliQz.isValid() && sensorValues->strSet.caliQw.isValid(); - if (kalmanInit) { - return multiplicativeKalmanFilter.mekfEst( + if (mekfStatus == MultiplicativeKalmanFilter::MEKF_UNINITIALIZED) { + mekfStatus = multiplicativeKalmanFilter.init( + mgmDataProcessed->mgmVecTot.value, mgmDataProcessed->mgmVecTot.isValid(), + susDataProcessed->susVecTot.value, susDataProcessed->susVecTot.isValid(), + susDataProcessed->sunIjkModel.value, susDataProcessed->sunIjkModel.isValid(), + mgmDataProcessed->magIgrfModel.value, mgmDataProcessed->magIgrfModel.isValid(), mekfData); + return mekfStatus; + } else { + mekfStatus = multiplicativeKalmanFilter.mekfEst( quatIB, quatIBValid, gyrDataProcessed->gyrVecTot.value, gyrDataProcessed->gyrVecTot.isValid(), mgmDataProcessed->mgmVecTot.value, mgmDataProcessed->mgmVecTot.isValid(), susDataProcessed->susVecTot.value, susDataProcessed->susVecTot.isValid(), susDataProcessed->sunIjkModel.value, susDataProcessed->sunIjkModel.isValid(), mgmDataProcessed->magIgrfModel.value, mgmDataProcessed->magIgrfModel.isValid(), acsParameters.onBoardParams.sampleTime, mekfData); - } else { - ReturnValue_t result; - result = multiplicativeKalmanFilter.init( - mgmDataProcessed->mgmVecTot.value, mgmDataProcessed->mgmVecTot.isValid(), - susDataProcessed->susVecTot.value, susDataProcessed->susVecTot.isValid(), - susDataProcessed->sunIjkModel.value, susDataProcessed->sunIjkModel.isValid(), - mgmDataProcessed->magIgrfModel.value, mgmDataProcessed->magIgrfModel.isValid(), mekfData); - kalmanInit = true; - return result; + return mekfStatus; } } void Navigation::resetMekf(acsctrl::MekfData *mekfData) { - multiplicativeKalmanFilter.reset(mekfData); + mekfStatus = multiplicativeKalmanFilter.reset(mekfData); } diff --git a/mission/controller/acs/Navigation.h b/mission/controller/acs/Navigation.h index e054bfbf..cf9e81e3 100644 --- a/mission/controller/acs/Navigation.h +++ b/mission/controller/acs/Navigation.h @@ -22,7 +22,7 @@ class Navigation { private: MultiplicativeKalmanFilter multiplicativeKalmanFilter; AcsParameters acsParameters; - bool kalmanInit = false; + ReturnValue_t mekfStatus = MultiplicativeKalmanFilter::MEKF_UNINITIALIZED; }; #endif /* ACS_NAVIGATION_H_ */ diff --git a/mission/controller/acs/SensorProcessing.h b/mission/controller/acs/SensorProcessing.h index 6fa1ab8c..cdd29d8b 100644 --- a/mission/controller/acs/SensorProcessing.h +++ b/mission/controller/acs/SensorProcessing.h @@ -1,7 +1,3 @@ -/******************************* - * EIVE Flight Software - * (c) 2022 IRS, Uni Stuttgart - *******************************/ #ifndef SENSORPROCESSING_H_ #define SENSORPROCESSING_H_ diff --git a/mission/controller/acs/SensorValues.cpp b/mission/controller/acs/SensorValues.cpp index e4026300..85815ae5 100644 --- a/mission/controller/acs/SensorValues.cpp +++ b/mission/controller/acs/SensorValues.cpp @@ -1,9 +1,3 @@ -/* - * SensorValues.cpp - * - * Created on: 30 Mar 2022 - * Author: rooob - */ #include "SensorValues.h" #include diff --git a/mission/controller/acs/SusConverter.cpp b/mission/controller/acs/SusConverter.cpp index f56ad32e..1a645270 100644 --- a/mission/controller/acs/SusConverter.cpp +++ b/mission/controller/acs/SusConverter.cpp @@ -1,16 +1,9 @@ -/* - * SusConverter.cpp - * - * Created on: 17.01.2022 - * Author: Timon Schwarz - */ - #include "SusConverter.h" #include #include #include -#include //for atan2 +#include #include diff --git a/mission/controller/acs/SusConverter.h b/mission/controller/acs/SusConverter.h index b3829827..046b0ca8 100644 --- a/mission/controller/acs/SusConverter.h +++ b/mission/controller/acs/SusConverter.h @@ -1,10 +1,3 @@ -/* - * SusConverter.h - * - * Created on: Sep 22, 2022 - * Author: marius - */ - #ifndef MISSION_CONTROLLER_ACS_SUSCONVERTER_H_ #define MISSION_CONTROLLER_ACS_SUSCONVERTER_H_ @@ -28,8 +21,6 @@ class SusConverter { private: float alphaBetaRaw[2]; //[°] - // float coeffAlpha[9][10]; - // float coeffBeta[9][10]; float alphaBetaCalibrated[2]; //[°] float sunVectorSensorFrame[3]; //[-] diff --git a/mission/controller/acs/util/MathOperations.h b/mission/controller/acs/util/MathOperations.h index b8d1fa4d..f8537740 100644 --- a/mission/controller/acs/util/MathOperations.h +++ b/mission/controller/acs/util/MathOperations.h @@ -300,6 +300,7 @@ class MathOperations { } static float matrixDeterminant(const T1 *inputMatrix, uint8_t size) { + /* do not use this. takes 300ms */ float det = 0; T1 matrix[size][size], submatrix[size - 1][size - 1]; for (uint8_t row = 0; row < size; row++) { @@ -329,9 +330,7 @@ class MathOperations { } static int inverseMatrix(const T1 *inputMatrix, T1 *inverse, uint8_t size) { - if (MathOperations::matrixDeterminant(inputMatrix, size) == 0) { - return 1; // Matrix is singular and not invertible - } + // Stopwatch stopwatch; T1 matrix[size][size], identity[size][size]; // reformat array to matrix for (uint8_t row = 0; row < size; row++) { @@ -346,7 +345,6 @@ class MathOperations { } // gauss-jordan algo // sort matrix such as no diag entry shall be 0 - // should not be needed as such a matrix has a det=0 for (uint8_t row = 0; row < size; row++) { if (matrix[row][row] == 0.0) { bool swaped = false; diff --git a/mission/core/GenericFactory.cpp b/mission/core/GenericFactory.cpp index b62c8af3..9e929e83 100644 --- a/mission/core/GenericFactory.cpp +++ b/mission/core/GenericFactory.cpp @@ -58,6 +58,7 @@ // TCP server includes #include "fsfw/osal/common/TcpTmTcBridge.h" #include "fsfw/osal/common/TcpTmTcServer.h" +#include "mission/tmtc/Service15TmStorage.h" #endif #endif @@ -83,7 +84,7 @@ EiveFaultHandler EIVE_FAULT_HANDLER; } // namespace cfdp void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFunnel** pusFunnel, - CfdpTmFunnel** cfdpFunnel) { + CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan) { // Framework objects new EventManager(objects::EVENT_MANAGER); auto healthTable = new HealthTable(objects::HEALTH_TABLE); @@ -95,6 +96,7 @@ void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFun auto* timeStamper = new CdsShortTimeStamper(objects::TIME_STAMPER); StorageManagerIF* tcStore; StorageManagerIF* tmStore; + StorageManagerIF* ipcStore; { PoolManager::LocalPoolConfig poolCfg = {{250, 16}, {250, 32}, {250, 64}, {150, 128}, {120, 1024}, {120, 2048}}; @@ -109,8 +111,8 @@ void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFun { PoolManager::LocalPoolConfig poolCfg = {{300, 16}, {200, 32}, {150, 64}, {150, 128}, - {100, 256}, {50, 512}, {50, 1024}, {50, 2048}}; - new PoolManager(objects::IPC_STORE, poolCfg); + {100, 256}, {50, 512}, {50, 1024}, {10, 2048}}; + ipcStore = new PoolManager(objects::IPC_STORE, poolCfg); } #if OBSW_ADD_TCPIP_SERVERS == 1 @@ -137,9 +139,11 @@ void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFun new CcsdsDistributor(config::EIVE_PUS_APID, objects::CCSDS_PACKET_DISTRIBUTOR); new PusDistributor(config::EIVE_PUS_APID, objects::PUS_PACKET_DISTRIBUTOR, ccsdsDistrib); - *cfdpFunnel = new CfdpTmFunnel(objects::CFDP_TM_FUNNEL, config::EIVE_CFDP_APID, *tmStore, 50); - *pusFunnel = new PusTmFunnel(objects::PUS_TM_FUNNEL, *timeStamper, *tmStore, - config::MAX_PUS_FUNNEL_QUEUE_DEPTH); + PusTmFunnel::FunnelCfg cfdpFunnelCfg(objects::CFDP_TM_FUNNEL, *tmStore, *ipcStore, 50); + *cfdpFunnel = new CfdpTmFunnel(cfdpFunnelCfg, config::EIVE_CFDP_APID); + PusTmFunnel::FunnelCfg pusFunnelCfg(objects::PUS_TM_FUNNEL, *tmStore, *ipcStore, + config::MAX_PUS_FUNNEL_QUEUE_DEPTH); + *pusFunnel = new PusTmFunnel(pusFunnelCfg, *timeStamper, sdcMan); #if OBSW_ADD_TCPIP_SERVERS == 1 #if OBSW_ADD_TMTC_UDP_SERVER == 1 (*cfdpFunnel)->addDestination("UDP Server", *udpBridge, 0); @@ -171,6 +175,7 @@ void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFun new Service11TelecommandScheduling( PsbParams(objects::PUS_SERVICE_11_TC_SCHEDULER, config::EIVE_PUS_APID, pus::PUS_SERVICE_11), ccsdsDistrib); + new Service15TmStorage(objects::PUS_SERVICE_15_TM_STORAGE, config::EIVE_PUS_APID, 10); new Service17Test( PsbParams(objects::PUS_SERVICE_17_TEST, config::EIVE_PUS_APID, pus::PUS_SERVICE_17)); new Service20ParameterManagement(objects::PUS_SERVICE_20_PARAMETERS, config::EIVE_PUS_APID, @@ -232,7 +237,7 @@ void ObjectFactory::createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t the std::array rwIds) { RwHelper rwHelper(rwIds); auto* rwAss = new RwAssembly(objects::RW_ASS, &pwrSwitcher, theSwitch, rwHelper); - for (uint8_t idx = 0; idx < rwIds.size(); idx++) { + for (size_t idx = 0; idx < rwIds.size(); idx++) { ReturnValue_t result = rws[idx]->connectModeTreeParent(*rwAss); if (result != returnvalue::OK) { sif::error << "Connecting RW " << static_cast(idx) << " to RW assembly failed" @@ -251,7 +256,7 @@ void ObjectFactory::createSusAssy(PowerSwitchIF& pwrSwitcher, objects::SUS_6_R_LOC_XFYBZM_PT_XF, objects::SUS_7_R_LOC_XBYBZM_PT_XB, objects::SUS_8_R_LOC_XBYBZB_PT_YB, objects::SUS_9_R_LOC_XBYBZB_PT_YF, objects::SUS_10_N_LOC_XMYBZF_PT_ZF, objects::SUS_11_R_LOC_XBYMZB_PT_ZB}; - SusAssHelper susAssHelper = SusAssHelper(susIds); + auto susAssHelper = SusAssHelper(susIds); auto susAss = new SusAssembly(objects::SUS_BOARD_ASS, &pwrSwitcher, susAssHelper); for (auto& sus : suses) { if (sus != nullptr) { @@ -287,8 +292,8 @@ void ObjectFactory::createAcsBoardAssy(PowerSwitchIF& pwrSwitcher, TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher) { TcsBoardHelper helper(RTD_INFOS); - TcsBoardAssembly* tcsBoardAss = new TcsBoardAssembly( - objects::TCS_BOARD_ASS, &pwrSwitcher, pcdu::Switches::PDU1_CH0_TCS_BOARD_3V3, helper); + auto* tcsBoardAss = new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher, + pcdu::Switches::PDU1_CH0_TCS_BOARD_3V3, helper); tcsBoardAss->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); return tcsBoardAss; } diff --git a/mission/core/GenericFactory.h b/mission/core/GenericFactory.h index 30f2d6bb..5902ff7b 100644 --- a/mission/core/GenericFactory.h +++ b/mission/core/GenericFactory.h @@ -2,6 +2,7 @@ #define MISSION_CORE_GENERICFACTORY_H_ #include +#include #include "fsfw/objectmanager/SystemObjectIF.h" #include "fsfw/power/PowerSwitchIF.h" @@ -37,7 +38,7 @@ const std::array, EiveMax31855::NUM_RTDS> RT namespace ObjectFactory { void produceGenericObjects(HealthTableIF** healthTable, PusTmFunnel** pusFunnel, - CfdpTmFunnel** cfdpFunnel); + CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan); void createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF& pwrSwitcher, HeaterHandler*& heaterHandler); diff --git a/mission/devices/PayloadPcduHandler.cpp b/mission/devices/PayloadPcduHandler.cpp index efefb16b..b93936d7 100644 --- a/mission/devices/PayloadPcduHandler.cpp +++ b/mission/devices/PayloadPcduHandler.cpp @@ -517,6 +517,9 @@ void PayloadPcduHandler::checkJsonFileInit() { if (not jsonFileInitComplete) { auto activeSd = sdcMan->getActiveSdCard(); if (activeSd and sdcMan->isSdCardUsable(activeSd.value())) { + if (sdcMan->getCurrentMountPrefix() == nullptr) { + return; + } params.initialize(sdcMan->getCurrentMountPrefix()); jsonFileInitComplete = true; } diff --git a/mission/devices/ScexDeviceHandler.cpp b/mission/devices/ScexDeviceHandler.cpp index 8da6d229..e2061a6b 100644 --- a/mission/devices/ScexDeviceHandler.cpp +++ b/mission/devices/ScexDeviceHandler.cpp @@ -214,6 +214,9 @@ ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, cons fileId = date_time_string(); std::ostringstream oss; auto prefix = sdcMan.getCurrentMountPrefix(); + if (prefix == nullptr) { + return returnvalue::FAILED; + } oss << prefix << "/scex/scex-" << cmdName << fileId << ".bin"; fileName = oss.str(); ofstream out(fileName, ofstream::binary); @@ -234,6 +237,9 @@ ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, cons fileId = date_time_string(); std::ostringstream oss; auto prefix = sdcMan.getCurrentMountPrefix(); + if (prefix == nullptr) { + return returnvalue::FAILED; + } oss << prefix << "/scex/scex-" << cmdName << fileId << ".bin"; fileName = oss.str(); fileNameSet = true; @@ -310,6 +316,16 @@ ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, cons } void ScexDeviceHandler::performOperationHook() { + auto mntPrefix = sdcMan.getCurrentMountPrefix(); + if (mntPrefix != nullptr) { + std::filesystem::path fullFilePath = mntPrefix; + fullFilePath /= "scex"; + bool fileExists = std::filesystem::exists(fullFilePath); + + if (not fileExists) { + std::filesystem::create_directory(fullFilePath); + } + } uint32_t remainingMillis = finishCountdown.getRemainingMillis(); if (commandActive and finishCountdown.hasTimedOut()) { triggerEvent(scex::EXPERIMENT_TIMEDOUT, currCmd, 0); @@ -373,13 +389,5 @@ void ScexDeviceHandler::setPowerSwitcher(PowerSwitchIF& powerSwitcher, power::Sw } ReturnValue_t ScexDeviceHandler::initializeAfterTaskCreation() { - auto mntPrefix = sdcMan.getCurrentMountPrefix(); - std::filesystem::path fullFilePath = mntPrefix; - fullFilePath /= "scex"; - bool fileExists = std::filesystem::exists(fullFilePath); - - if (not fileExists) { - std::filesystem::create_directory(fullFilePath); - } return DeviceHandlerBase::initializeAfterTaskCreation(); } diff --git a/mission/devices/devicedefinitions/GPSDefinitions.h b/mission/devices/devicedefinitions/GPSDefinitions.h index 8acb77e9..80e2861a 100644 --- a/mission/devices/devicedefinitions/GPSDefinitions.h +++ b/mission/devices/devicedefinitions/GPSDefinitions.h @@ -67,6 +67,7 @@ class GpsPrimaryDataset : public StaticLocalDataSet<18> { private: friend class GpsHyperionLinuxController; + friend class GpsCtrlDummy; GpsPrimaryDataset(HasLocalDataPoolIF* hkOwner) : StaticLocalDataSet(hkOwner, GpsHyperion::DATASET_ID) {} }; diff --git a/mission/memory/SdCardMountedIF.h b/mission/memory/SdCardMountedIF.h index ac705e8d..1e76b9b7 100644 --- a/mission/memory/SdCardMountedIF.h +++ b/mission/memory/SdCardMountedIF.h @@ -9,7 +9,7 @@ class SdCardMountedIF { public: virtual ~SdCardMountedIF(){}; - virtual const std::string& getCurrentMountPrefix() const = 0; + virtual const char* getCurrentMountPrefix() const = 0; virtual bool isSdCardUsable(std::optional sdCard) = 0; virtual std::optional getPreferredSdCard() const = 0; virtual void setActiveSdCard(sd::SdCard sdCard) = 0; diff --git a/mission/tmtc/CMakeLists.txt b/mission/tmtc/CMakeLists.txt index bb92a84e..c4c93ab6 100644 --- a/mission/tmtc/CMakeLists.txt +++ b/mission/tmtc/CMakeLists.txt @@ -1,4 +1,10 @@ target_sources( ${LIB_EIVE_MISSION} - PRIVATE CcsdsIpCoreHandler.cpp VirtualChannel.cpp TmFunnelHandler.cpp - TmFunnelBase.cpp CfdpTmFunnel.cpp PusTmFunnel.cpp) + PRIVATE CcsdsIpCoreHandler.cpp + VirtualChannel.cpp + TmFunnelHandler.cpp + TmFunnelBase.cpp + CfdpTmFunnel.cpp + Service15TmStorage.cpp + PersistentTmStore.cpp + PusTmFunnel.cpp) diff --git a/mission/tmtc/CfdpTmFunnel.cpp b/mission/tmtc/CfdpTmFunnel.cpp index 32dba7fe..89d7c105 100644 --- a/mission/tmtc/CfdpTmFunnel.cpp +++ b/mission/tmtc/CfdpTmFunnel.cpp @@ -4,9 +4,8 @@ #include "fsfw/tmtcpacket/ccsds/SpacePacketCreator.h" #include "fsfw/tmtcservices/TmTcMessage.h" -CfdpTmFunnel::CfdpTmFunnel(object_id_t objectId, uint16_t cfdpInCcsdsApid, - StorageManagerIF& tmStore, uint32_t messageDepth) - : TmFunnelBase(objectId, tmStore, messageDepth), cfdpInCcsdsApid(cfdpInCcsdsApid) {} +CfdpTmFunnel::CfdpTmFunnel(TmFunnelBase::FunnelCfg cfg, uint16_t cfdpInCcsdsApid) + : TmFunnelBase(cfg), cfdpInCcsdsApid(cfdpInCcsdsApid) {} const char* CfdpTmFunnel::getName() const { return "CFDP TM Funnel"; } diff --git a/mission/tmtc/CfdpTmFunnel.h b/mission/tmtc/CfdpTmFunnel.h index e294956a..717573a0 100644 --- a/mission/tmtc/CfdpTmFunnel.h +++ b/mission/tmtc/CfdpTmFunnel.h @@ -12,8 +12,7 @@ class CfdpTmFunnel : public TmFunnelBase { public: - CfdpTmFunnel(object_id_t objectId, uint16_t cfdpInCcsdsApid, StorageManagerIF& tmStore, - uint32_t messageDepth); + CfdpTmFunnel(TmFunnelBase::FunnelCfg cfg, uint16_t cfdpInCcsdsApid); [[nodiscard]] const char* getName() const override; ReturnValue_t performOperation(uint8_t opCode); ReturnValue_t initialize() override; diff --git a/mission/tmtc/PersistentTmStore.cpp b/mission/tmtc/PersistentTmStore.cpp new file mode 100644 index 00000000..6ceed609 --- /dev/null +++ b/mission/tmtc/PersistentTmStore.cpp @@ -0,0 +1,362 @@ +#include "PersistentTmStore.h" + +#include + +#include +#include +#include +#include +#include + +#include "fsfw/ipc/CommandMessage.h" +#include "fsfw/ipc/QueueFactory.h" +#include "fsfw/tmstorage/TmStoreMessage.h" + +using namespace returnvalue; + +PersistentTmStore::PersistentTmStore(object_id_t objectId, const char* baseDir, + std::string baseName, RolloverInterval intervalUnit, + uint32_t intervalCount, StorageManagerIF& tmStore, + SdCardMountedIF& sdcMan) + : SystemObject(objectId), + baseDir(baseDir), + baseName(std::move(baseName)), + sdcMan(sdcMan), + tmStore(tmStore) { + tcQueue = QueueFactory::instance()->createMessageQueue(); + calcDiffSeconds(intervalUnit, intervalCount); +} + +ReturnValue_t PersistentTmStore::assignAndOrCreateMostRecentFile() { + using namespace std::filesystem; + for (auto const& file : directory_iterator(basePath)) { + if (file.is_directory()) { + continue; + } + auto pathStr = file.path().string(); + if (pathStr.find(baseName) == std::string::npos) { + continue; + } + unsigned int underscorePos = pathStr.find_last_of('_'); + std::string stampStr = pathStr.substr(underscorePos + 1); + struct tm time {}; + if (nullptr == strptime(stampStr.c_str(), FILE_DATE_FORMAT, &time)) { + sif::error << "PersistentTmStore::assignOrCreateMostRecentFile: Error reading timestamp" + << std::endl; + // Delete the file and re-create it. + activeFile = std::nullopt; + std::filesystem::remove(file.path()); + break; + } + time_t fileEpoch = timegm(&time); + // There is still a file within the active time window, so re-use that file for new TMs to + // store. + if (fileEpoch + static_cast(rolloverDiffSeconds) > currentTv.tv_sec) { + activeFileTv.tv_sec = fileEpoch; + activeFile = file.path(); + break; + } + } + if (not activeFile.has_value()) { + return createMostRecentFile(std::nullopt); + } + return returnvalue::OK; +} + +ReturnValue_t PersistentTmStore::handleCommandQueue(StorageManagerIF& ipcStore, + TmFunnelBase& tmFunnel) { + CommandMessage cmdMessage; + ReturnValue_t result = tcQueue->receiveMessage(&cmdMessage); + if (result == MessageQueueIF::EMPTY) { + return returnvalue::OK; + } + if (result != returnvalue::OK) { + return result; + } + if (cmdMessage.getMessageType() == messagetypes::TM_STORE) { + Command_t cmd = cmdMessage.getCommand(); + if (cmd == TmStoreMessage::DELETE_STORE_CONTENT_TIME) { + Clock::getClock_timeval(¤tTv); + store_address_t storeId = TmStoreMessage::getStoreId(&cmdMessage); + auto accessor = ipcStore.getData(storeId); + uint32_t deleteUpToUnixSeconds = 0; + size_t size = accessor.second.size(); + SerializeAdapter::deSerialize(&deleteUpToUnixSeconds, accessor.second.data(), &size, + SerializeIF::Endianness::NETWORK); + deleteUpTo(deleteUpToUnixSeconds); + } else if (cmd == TmStoreMessage::DOWNLINK_STORE_CONTENT_TIME) { + Clock::getClock_timeval(¤tTv); + store_address_t storeId = TmStoreMessage::getStoreId(&cmdMessage); + auto accessor = ipcStore.getData(storeId); + if (accessor.second.size() < 8) { + return returnvalue::FAILED; + } + uint32_t dumpFromUnixSeconds; + uint32_t dumpUntilUnixSeconds; + size_t size = 8; + SerializeAdapter::deSerialize(&dumpFromUnixSeconds, accessor.second.data(), &size, + SerializeIF::Endianness::NETWORK); + SerializeAdapter::deSerialize(&dumpUntilUnixSeconds, accessor.second.data() + 4, &size, + SerializeIF::Endianness::NETWORK); + dumpFromUpTo(dumpFromUnixSeconds, dumpUntilUnixSeconds, tmFunnel); + } + } + return returnvalue::OK; +} + +ReturnValue_t PersistentTmStore::passPacket(PusTmReader& reader) { + bool inApidList = false; + if (filter.apid) { + auto& apidFilter = filter.apid.value(); + if (std::find(apidFilter.begin(), apidFilter.end(), reader.getApid()) != apidFilter.end()) { + if (not filter.serviceSubservices and not filter.services) { + return storePacket(reader); + } + inApidList = true; + } + } + std::pair serviceSubservice; + serviceSubservice.first = reader.getService(); + serviceSubservice.second = reader.getSubService(); + if (filter.services) { + auto& serviceFilter = filter.services.value(); + if (std::find(serviceFilter.begin(), serviceFilter.end(), serviceSubservice.first) != + serviceFilter.end()) { + if (filter.apid and inApidList) { + return storePacket(reader); + } + } + } + if (filter.serviceSubservices) { + auto& serviceSubserviceFilter = filter.serviceSubservices.value(); + if (std::find(serviceSubserviceFilter.begin(), serviceSubserviceFilter.end(), + serviceSubservice) != serviceSubserviceFilter.end()) { + if (filter.apid and inApidList) { + return storePacket(reader); + } + } + } + return returnvalue::OK; +} + +void PersistentTmStore::dumpFrom(uint32_t fromUnixSeconds, TmFunnelBase& tmFunnel) { + return dumpFromUpTo(fromUnixSeconds, currentTv.tv_sec, tmFunnel); +} + +ReturnValue_t PersistentTmStore::storePacket(PusTmReader& reader) { + using namespace std::filesystem; + if (baseDirUninitialized) { + updateBaseDir(); + } + Clock::getClock_timeval(¤tTv); + // It is assumed here that the filesystem is usable. + if (not activeFile.has_value()) { + ReturnValue_t result = assignAndOrCreateMostRecentFile(); + if (result != returnvalue::OK) { + return result; + } + } + + bool createNewFile = false; + std::optional suffix = std::nullopt; + if (currentTv.tv_sec > activeFileTv.tv_sec + static_cast(rolloverDiffSeconds)) { + createNewFile = true; + currentSameSecNumber = 0; + } else if (file_size(activeFile.value()) + reader.getFullPacketLen() > fileBuf.size()) { + createNewFile = true; + if (currentSameSecNumber >= MAX_FILES_IN_ONE_SECOND) { + currentSameSecNumber = 0; + } + if (currentTv.tv_sec == activeFileTv.tv_sec) { + suffix = currentSameSecNumber++; + } else { + currentSameSecNumber = 0; + } + } + if (createNewFile) { + createMostRecentFile(suffix); + } + + // Rollover conditions were handled, write to file now + std::ofstream of(activeFile.value(), std::ios::app | std::ios::binary); + of.write(reinterpret_cast(reader.getFullData()), + static_cast(reader.getFullPacketLen())); + return returnvalue::OK; +} + +MessageQueueId_t PersistentTmStore::getCommandQueue() const { return tcQueue->getId(); } + +void PersistentTmStore::calcDiffSeconds(RolloverInterval intervalUnit, uint32_t intervalCount) { + if (intervalUnit == RolloverInterval::MINUTELY) { + rolloverDiffSeconds = 60 * intervalCount; + } else if (intervalUnit == RolloverInterval::HOURLY) { + rolloverDiffSeconds = 60 * 60 * intervalCount; + } else if (intervalUnit == RolloverInterval::DAILY) { + rolloverDiffSeconds = 60 * 60 * 24 * intervalCount; + } +} + +bool PersistentTmStore::updateBaseDir() { + using namespace std::filesystem; + const char* currentPrefix = sdcMan.getCurrentMountPrefix(); + if (currentPrefix == nullptr) { + return false; + } + basePath = path(currentPrefix) / baseDir / baseName; + if (not exists(basePath)) { + create_directories(basePath); + } + baseDirUninitialized = false; + return true; +} + +void PersistentTmStore::addApid(uint16_t apid) { + if (not filter.apid) { + filter.apid = std::vector({apid}); + return; + } + filter.apid.value().push_back(apid); +} + +void PersistentTmStore::addService(uint8_t service) { + if (not filter.services) { + filter.services = std::vector({service}); + return; + } + filter.services.value().push_back(service); +} + +void PersistentTmStore::addServiceSubservice(uint8_t service, uint8_t subservice) { + if (not filter.serviceSubservices) { + filter.serviceSubservices = + std::vector>({std::pair(service, subservice)}); + return; + } + filter.serviceSubservices.value().emplace_back(service, subservice); +} + +void PersistentTmStore::deleteUpTo(uint32_t unixSeconds) { + using namespace std::filesystem; + for (auto const& file : directory_iterator(basePath)) { + if (file.is_directory() or (activeFile.has_value() and (activeFile.value() == file.path()))) { + continue; + } + // Convert file time to the UNIX epoch + struct tm fileTime {}; + if (pathToTm(file.path(), fileTime) != returnvalue::OK) { + sif::error << "Time extraction for " << file << "failed" << std::endl; + continue; + } + time_t fileEpoch = timegm(&fileTime); + if (fileEpoch + rolloverDiffSeconds < unixSeconds) { + std::filesystem::remove(file.path()); + } + } +} + +void PersistentTmStore::dumpFromUpTo(uint32_t fromUnixSeconds, uint32_t upToUnixSeconds, + TmFunnelBase& funnel) { + using namespace std::filesystem; + for (auto const& file : directory_iterator(basePath)) { + if (file.is_directory()) { + continue; + } + struct tm fileTime {}; + if (pathToTm(file.path(), fileTime) != returnvalue::OK) { + sif::error << "Time extraction for file " << file << "failed" << std::endl; + continue; + } + auto fileEpoch = static_cast(timegm(&fileTime)); + if ((fileEpoch > fromUnixSeconds) and (fileEpoch + rolloverDiffSeconds <= upToUnixSeconds)) { + fileToPackets(file, fileEpoch, funnel); + } + } +} + +ReturnValue_t PersistentTmStore::pathToTm(const std::filesystem::path& path, struct tm& time) { + auto pathStr = path.string(); + size_t splitChar = pathStr.find('_'); + auto timeOnlyStr = pathStr.substr(splitChar + 1); + if (nullptr == strptime(timeOnlyStr.c_str(), FILE_DATE_FORMAT, &time)) { + return returnvalue::FAILED; + } + return returnvalue::OK; +} + +void PersistentTmStore::fileToPackets(const std::filesystem::path& path, uint32_t unixStamp, + TmFunnelBase& funnel) { + store_address_t storeId; + TmTcMessage message; + size_t size = std::filesystem::file_size(path); + if (size < 6) { + // Can't even read the CCSDS header + return; + } + std::ifstream ifile(path, std::ios::binary); + ifile.read(reinterpret_cast(fileBuf.data()), static_cast(size)); + size_t currentIdx = 0; + while (currentIdx < size) { + PusTmReader reader(&timeReader, fileBuf.data(), fileBuf.size()); + // CRC check to fully ensure this is a valid TM + ReturnValue_t result = reader.parseDataWithCrcCheck(); + if (result == returnvalue::OK) { + result = tmStore.addData(&storeId, fileBuf.data() + currentIdx, reader.getFullPacketLen()); + if (result != returnvalue::OK) { + continue; + } + funnel.sendPacketToDestinations(storeId, message, fileBuf.data() + currentIdx, + reader.getFullPacketLen()); + currentIdx += reader.getFullPacketLen(); + } else { + sif::error << "Parsing of PUS TM failed with code " << result << std::endl; + triggerEvent(POSSIBLE_FILE_CORRUPTION, result, unixStamp); + // Stop for now, do not really know where to continue and we do not trust the file anymore. + break; + } + } +} + +ReturnValue_t PersistentTmStore::createMostRecentFile(std::optional suffix) { + using namespace std::filesystem; + unsigned currentIdx = 0; + path pathStart = basePath / baseName; + memcpy(fileBuf.data() + currentIdx, pathStart.c_str(), pathStart.string().length()); + currentIdx += pathStart.string().length(); + fileBuf[currentIdx] = '_'; + currentIdx += 1; + time_t epoch = currentTv.tv_sec; + struct tm* time = gmtime(&epoch); + size_t writtenBytes = strftime(reinterpret_cast(fileBuf.data() + currentIdx), + fileBuf.size(), FILE_DATE_FORMAT, time); + if (writtenBytes == 0) { + sif::error << "PersistentTmStore::createMostRecentFile: Could not create file timestamp" + << std::endl; + return returnvalue::FAILED; + } + currentIdx += writtenBytes; + char* res = strcpy(reinterpret_cast(fileBuf.data() + currentIdx), ".bin"); + if (res == nullptr) { + return returnvalue::FAILED; + } + currentIdx += 4; + if (suffix.has_value()) { + std::string fullSuffix = "." + std::to_string(suffix.value()); + res = strcpy(reinterpret_cast(fileBuf.data() + currentIdx), fullSuffix.c_str()); + if (res == nullptr) { + return returnvalue::FAILED; + } + currentIdx += fullSuffix.size(); + } + + path newPath(std::string(reinterpret_cast(fileBuf.data()), currentIdx)); + std::ofstream of(newPath, std::ios::binary); + activeFile = newPath; + activeFileTv = currentTv; + return returnvalue::OK; +} + +ReturnValue_t PersistentTmStore::initializeTmStore() { + Clock::getClock_timeval(¤tTv); + updateBaseDir(); + return assignAndOrCreateMostRecentFile(); +} diff --git a/mission/tmtc/PersistentTmStore.h b/mission/tmtc/PersistentTmStore.h new file mode 100644 index 00000000..d8bc9acf --- /dev/null +++ b/mission/tmtc/PersistentTmStore.h @@ -0,0 +1,87 @@ +#ifndef MISSION_TMTC_TMSTOREBACKEND_H_ +#define MISSION_TMTC_TMSTOREBACKEND_H_ + +#include +#include +#include +#include +#include +#include + +#include + +#include "TmFunnelBase.h" +#include "eive/eventSubsystemIds.h" + +struct PacketFilter { + std::optional> apid; + std::optional> services; + std::optional>> serviceSubservices; +}; + +enum class RolloverInterval { MINUTELY, HOURLY, DAILY }; + +class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject { + public: + static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::PERSISTENT_TM_STORE; + + //! [EXPORT] : [COMMENT] + //! P1: Result code of TM packet parser. + //! P2: Timestamp of possibly corrupt file as a unix timestamp. + static constexpr Event POSSIBLE_FILE_CORRUPTION = + event::makeEvent(SUBSYSTEM_ID, 0, severity::LOW); + PersistentTmStore(object_id_t objectId, const char* baseDir, std::string baseName, + RolloverInterval intervalUnit, uint32_t intervalCount, + StorageManagerIF& tmStore, SdCardMountedIF& sdcMan); + + ReturnValue_t initializeTmStore(); + ReturnValue_t handleCommandQueue(StorageManagerIF& ipcStore, TmFunnelBase& tmFunnel); + + void addApid(uint16_t apid); + void addService(uint8_t service); + void addServiceSubservice(uint8_t service, uint8_t subservice); + + void deleteUpTo(uint32_t unixSeconds); + void dumpFrom(uint32_t fromUnixSeconds, TmFunnelBase& tmFunnel); + void dumpFromUpTo(uint32_t fromUnixSeconds, uint32_t upToUnixSeconds, TmFunnelBase& tmFunnel); + + ReturnValue_t passPacket(PusTmReader& reader); + + private: + static constexpr uint8_t MAX_FILES_IN_ONE_SECOND = 10; + static constexpr size_t MAX_FILESIZE = 8192; + // ISO8601 timestamp. + static constexpr char FILE_DATE_FORMAT[] = "%FT%H%M%SZ"; + + MessageQueueIF* tcQueue; + PacketFilter filter; + CdsShortTimeStamper timeReader; + bool baseDirUninitialized = true; + const char* baseDir; + std::string baseName; + uint8_t currentSameSecNumber = 0; + std::filesystem::path basePath; + uint32_t rolloverDiffSeconds = 0; + std::array fileBuf{}; + timeval currentTv; + timeval activeFileTv{}; + std::optional activeFile; + SdCardMountedIF& sdcMan; + StorageManagerIF& tmStore; + + /** + * To get the queue where commands shall be sent. + * @return Id of command queue. + */ + [[nodiscard]] MessageQueueId_t getCommandQueue() const override; + + void calcDiffSeconds(RolloverInterval intervalUnit, uint32_t intervalCount); + ReturnValue_t createMostRecentFile(std::optional suffix); + static ReturnValue_t pathToTm(const std::filesystem::path& path, struct tm& time); + void fileToPackets(const std::filesystem::path& path, uint32_t unixStamp, TmFunnelBase& funnel); + bool updateBaseDir(); + ReturnValue_t assignAndOrCreateMostRecentFile(); + ReturnValue_t storePacket(PusTmReader& reader); +}; + +#endif /* MISSION_TMTC_TMSTOREBACKEND_H_ */ diff --git a/mission/tmtc/PusTmFunnel.cpp b/mission/tmtc/PusTmFunnel.cpp index 974cdca0..08a047a0 100644 --- a/mission/tmtc/PusTmFunnel.cpp +++ b/mission/tmtc/PusTmFunnel.cpp @@ -1,22 +1,87 @@ #include "PusTmFunnel.h" +#include "eive/definitions.h" +#include "eive/objects.h" +#include "fsfw/ipc/CommandMessage.h" #include "fsfw/ipc/QueueFactory.h" #include "fsfw/objectmanager.h" +#include "fsfw/pus/Service5EventReporting.h" +#include "fsfw/tmstorage/TmStoreMessage.h" #include "fsfw/tmtcpacket/pus/tm/PusTmZcWriter.h" +#include "tmtc/pusIds.h" -PusTmFunnel::PusTmFunnel(object_id_t objectId, TimeReaderIF &timeReader, StorageManagerIF &tmStore, - uint32_t messageDepth) - : TmFunnelBase(objectId, tmStore, messageDepth), timeReader(timeReader) {} +PusTmFunnel::PusTmFunnel(TmFunnelBase::FunnelCfg cfg, TimeReaderIF &timeReader, + SdCardMountedIF &sdcMan) + : TmFunnelBase(cfg), + timeReader(timeReader), + miscStore(objects::MISC_TM_STORE, "tm", "misc", RolloverInterval::HOURLY, 2, tmStore, sdcMan), + okStore(objects::OK_TM_STORE, "tm", "ok", RolloverInterval::MINUTELY, 30, tmStore, sdcMan), + notOkStore(objects::NOT_OK_TM_STORE, "tm", "nok", RolloverInterval::MINUTELY, 30, tmStore, + sdcMan), + hkStore(objects::HK_TM_STORE, "tm", "hk", RolloverInterval::MINUTELY, 15, tmStore, sdcMan), + sdcMan(sdcMan) { + Clock::getClock_timeval(¤tTv); + Clock::getUptime(&lastTvUpdate); + hkStore.addApid(config::EIVE_PUS_APID); + hkStore.addService(pus::PUS_SERVICE_3); + miscStore.addApid(config::EIVE_PUS_APID); + miscStore.addService(pus::PUS_SERVICE_17); + miscStore.addService(pus::PUS_SERVICE_2); + miscStore.addService(pus::PUS_SERVICE_200); + miscStore.addService(pus::PUS_SERVICE_201); + miscStore.addService(pus::PUS_SERVICE_9); + miscStore.addService(pus::PUS_SERVICE_20); + okStore.addApid(config::EIVE_PUS_APID); + okStore.addServiceSubservice(pus::PUS_SERVICE_5, + Service5EventReporting::Subservice::NORMAL_REPORT); + okStore.addService(pus::PUS_SERVICE_8); + okStore.addServiceSubservice(pus::PUS_SERVICE_1, 1); + okStore.addServiceSubservice(pus::PUS_SERVICE_1, 3); + okStore.addServiceSubservice(pus::PUS_SERVICE_1, 5); + okStore.addServiceSubservice(pus::PUS_SERVICE_1, 7); + notOkStore.addApid(config::EIVE_PUS_APID); + notOkStore.addServiceSubservice(pus::PUS_SERVICE_5, 2); + notOkStore.addServiceSubservice(pus::PUS_SERVICE_5, 3); + notOkStore.addServiceSubservice(pus::PUS_SERVICE_5, 4); + notOkStore.addServiceSubservice(pus::PUS_SERVICE_1, 2); + notOkStore.addServiceSubservice(pus::PUS_SERVICE_1, 4); + notOkStore.addServiceSubservice(pus::PUS_SERVICE_1, 6); + notOkStore.addServiceSubservice(pus::PUS_SERVICE_1, 8); +} PusTmFunnel::~PusTmFunnel() = default; ReturnValue_t PusTmFunnel::performOperation(uint8_t) { + CommandMessage cmdMessage; + ReturnValue_t result; + try { + result = okStore.handleCommandQueue(ipcStore, *this); + if (result != returnvalue::OK) { + sif::error << "PusTmFunnel::performOperation: Issue handling OK store command" << std::endl; + } + result = notOkStore.handleCommandQueue(ipcStore, *this); + if (result != returnvalue::OK) { + sif::error << "PusTmFunnel::performOperation: Issue handling NOT OK store command" + << std::endl; + } + result = hkStore.handleCommandQueue(ipcStore, *this); + if (result != returnvalue::OK) { + sif::error << "PusTmFunnel::performOperation: Issue handling HK store command" << std::endl; + } + result = miscStore.handleCommandQueue(ipcStore, *this); + if (result != returnvalue::OK) { + sif::error << "PusTmFunnel::performOperation: Issue handling MISC store command" << std::endl; + } + } catch (const std::bad_optional_access &e) { + sif::error << e.what() << "when handling TM store command" << std::endl; + } + TmTcMessage currentMessage; unsigned int count = 0; - ReturnValue_t status = tmQueue->receiveMessage(¤tMessage); - while (status == returnvalue::OK) { - status = handlePacket(currentMessage); - if (status != returnvalue::OK) { + result = tmQueue->receiveMessage(¤tMessage); + while (result == returnvalue::OK) { + result = handleTmPacket(currentMessage); + if (result != returnvalue::OK) { sif::warning << "TmFunnel packet handling failed" << std::endl; break; } @@ -25,16 +90,16 @@ ReturnValue_t PusTmFunnel::performOperation(uint8_t) { sif::error << "PusTmFunnel: Possible message storm detected" << std::endl; break; } - status = tmQueue->receiveMessage(¤tMessage); + result = tmQueue->receiveMessage(¤tMessage); } - if (status == MessageQueueIF::EMPTY) { + if (result == MessageQueueIF::EMPTY) { return returnvalue::OK; } - return status; + return result; } -ReturnValue_t PusTmFunnel::handlePacket(TmTcMessage &message) { +ReturnValue_t PusTmFunnel::handleTmPacket(TmTcMessage &message) { uint8_t *packetData = nullptr; size_t size = 0; store_address_t origStoreId = message.getStorageId(); @@ -54,36 +119,38 @@ ReturnValue_t PusTmFunnel::handlePacket(TmTcMessage &message) { sourceSequenceCount = sourceSequenceCount % ccsds::LIMIT_SEQUENCE_COUNT; packet.updateErrorControl(); - for (unsigned int idx = 0; idx < destinations.size(); idx++) { - const auto &dest = destinations[idx]; - if (destinations.size() > 1) { - if (idx < destinations.size() - 1) { - // Create copy of data to ensure each TM recipient has its own copy. That way, we don't need - // to bother with send order and where the data is deleted. - store_address_t storeId; - result = tmStore.addData(&storeId, packetData, size); - if (result == returnvalue::OK) { - message.setStorageId(storeId); - } else { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PusTmFunnel::handlePacket: Store too full to create data copy" - << std::endl; -#endif - } - } else { - message.setStorageId(origStoreId); - } - } - result = tmQueue->sendMessage(dest.queueId, &message); - if (result != returnvalue::OK) { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PusTmFunnel::handlePacket: Error sending TM to downlink handler " << dest.name - << " failed" << std::endl; -#endif - tmStore.deleteData(message.getStorageId()); - } + timeval currentUptime{}; + Clock::getUptime(¤tUptime); + if (currentUptime.tv_sec - lastTvUpdate.tv_sec > + static_cast(TV_UPDATE_INTERVAL_SECS)) { + Clock::getClock_timeval(¤tTv); + lastTvUpdate = currentUptime; } - return result; + + bool sdcUsable = sdcMan.isSdCardUsable(std::nullopt); + initStoresIfPossible(sdcUsable); + if (sdcUsable) { + miscStore.passPacket(packet); + okStore.passPacket(packet); + notOkStore.passPacket(packet); + hkStore.passPacket(packet); + } + return sendPacketToDestinations(origStoreId, message, packetData, size); } const char *PusTmFunnel::getName() const { return "PUS TM Funnel"; } + +void PusTmFunnel::initStoresIfPossible(bool sdCardUsable) { + if (not storesInitialized and sdCardUsable and sdcMan.getCurrentMountPrefix() != nullptr) { + miscStore.initializeTmStore(); + okStore.initializeTmStore(); + hkStore.initializeTmStore(); + notOkStore.initializeTmStore(); + storesInitialized = true; + } +} + +ReturnValue_t PusTmFunnel::initialize() { + initStoresIfPossible(sdcMan.isSdCardUsable(std::nullopt)); + return returnvalue::OK; +} diff --git a/mission/tmtc/PusTmFunnel.h b/mission/tmtc/PusTmFunnel.h index ca9a6016..ab6a9480 100644 --- a/mission/tmtc/PusTmFunnel.h +++ b/mission/tmtc/PusTmFunnel.h @@ -10,33 +10,43 @@ #include +#include "PersistentTmStore.h" +#include "fsfw/ipc/CommandMessage.h" #include "fsfw/timemanager/TimeReaderIF.h" /** * @brief TM Recipient. * @details - * TODO: Add support for TM storage by using the (or a) LIVE flag provided by the CCSDS or Syrlinks - * handler. If we are in LIVE TM mode, forward TM to downlink destination directly. Otherwise, - * forward to TM storage backend which stores TMs into files. - * Main telemetry receiver. All generated telemetry is funneled into - * this object. + * Main telemetry receiver. All generated telemetry is funneled into this object. * @ingroup utility * @author J. Meier, R. Mueller */ class PusTmFunnel : public TmFunnelBase { public: - explicit PusTmFunnel(object_id_t objectId, TimeReaderIF &timeReader, StorageManagerIF &tmStore, - uint32_t messageDepth = 10); + PusTmFunnel(TmFunnelBase::FunnelCfg cfg, TimeReaderIF &timeReader, SdCardMountedIF &sdcMan); [[nodiscard]] const char *getName() const override; ~PusTmFunnel() override; ReturnValue_t performOperation(uint8_t operationCode); private: + // Update TV stamp every 5 minutes + static constexpr dur_millis_t TV_UPDATE_INTERVAL_SECS = 60 * 5; + uint16_t sourceSequenceCount = 0; TimeReaderIF &timeReader; + bool storesInitialized = false; + timeval currentTv{}; + timeval lastTvUpdate{}; + PersistentTmStore miscStore; + PersistentTmStore okStore; + PersistentTmStore notOkStore; + PersistentTmStore hkStore; + SdCardMountedIF &sdcMan; - ReturnValue_t handlePacket(TmTcMessage &message); + ReturnValue_t handleTmPacket(TmTcMessage &message); + void initStoresIfPossible(bool sdCardUsable); + ReturnValue_t initialize() override; }; #endif // FSFW_EXAMPLE_COMMON_PUSTMFUNNEL_H diff --git a/mission/tmtc/Service15TmStorage.cpp b/mission/tmtc/Service15TmStorage.cpp new file mode 100644 index 00000000..1c9b8611 --- /dev/null +++ b/mission/tmtc/Service15TmStorage.cpp @@ -0,0 +1,85 @@ +#include "Service15TmStorage.h" + +#include +#include + +#include "eive/objects.h" +#include "fsfw/tmstorage/TmStoreMessage.h" + +using namespace returnvalue; + +Service15TmStorage::Service15TmStorage(object_id_t objectId, uint16_t apid, + uint8_t numParallelCommands, uint16_t commandTimeoutSecs, + size_t queueDepth) + : CommandingServiceBase(objectId, apid, "PUS Service 15", 15, numParallelCommands, + commandTimeoutSecs, queueDepth) {} + +ReturnValue_t Service15TmStorage::isValidSubservice(uint8_t subservice) { + switch (subservice) { + case (Subservices::DELETE_UP_TO): + case (Subservices::START_BY_TIME_RANGE_RETRIEVAL): { + return OK; + } + default: { + return FAILED; + } + } +} + +ReturnValue_t Service15TmStorage::getMessageQueueAndObject(uint8_t subservice, + const uint8_t *tcData, size_t tcDataLen, + MessageQueueId_t *id, + object_id_t *objectId) { + if (tcDataLen < 4) { + return CommandingServiceBase::INVALID_TC; + } + SerializeAdapter::deSerialize(objectId, &tcData, &tcDataLen, SerializeIF::Endianness::NETWORK); + auto *frontendIF = ObjectManager::instance()->get(*objectId); + if (frontendIF == nullptr) { + return CommandingServiceBase::INVALID_OBJECT; + } + *id = frontendIF->getCommandQueue(); + return OK; +} + +ReturnValue_t Service15TmStorage::prepareCommand(CommandMessage *message, uint8_t subservice, + const uint8_t *tcData, size_t tcDataLen, + uint32_t *state, object_id_t objectId) { + if (subservice == Subservices::START_BY_TIME_RANGE_RETRIEVAL) { + // TODO: Hardcoded to UNIX timestamps.. Should allow arbitrary timestamp and let receiver + // to time reading and reply handling + if (tcDataLen != 12) { + return INVALID_TC; + } + store_address_t storeId; + ReturnValue_t result = ipcStore->addData(&storeId, tcData + 4, tcDataLen - 4); + if (result != OK) { + return result; + } + // Store timestamps + TmStoreMessage::setDownlinkContentTimeMessage(message, storeId); + return CommandingServiceBase::EXECUTION_COMPLETE; + } else if (subservice == Subservices::DELETE_UP_TO) { + // TODO: Hardcoded to UNIX timestamps.. Should allow arbitrary timestamp and let receiver + // to time reading and reply handling + if (tcDataLen != 8) { + return INVALID_TC; + } + store_address_t storeId; + ReturnValue_t result = ipcStore->addData(&storeId, tcData + 4, tcDataLen - 4); + if (result != OK) { + return result; + } + // Store timestamps + TmStoreMessage::setDeleteContentTimeMessage(message, storeId); + return CommandingServiceBase::EXECUTION_COMPLETE; + } + return OK; +} + +ReturnValue_t Service15TmStorage::handleReply(const CommandMessage *reply, + Command_t previousCommand, uint32_t *state, + CommandMessage *optionalNextCommand, + object_id_t objectId, bool *isStep) { + return OK; +} diff --git a/mission/tmtc/Service15TmStorage.h b/mission/tmtc/Service15TmStorage.h new file mode 100644 index 00000000..33be0634 --- /dev/null +++ b/mission/tmtc/Service15TmStorage.h @@ -0,0 +1,24 @@ +#ifndef MISSION_TMTC_SERVICE15TMSTORAGE_H_ +#define MISSION_TMTC_SERVICE15TMSTORAGE_H_ + +#include + +class Service15TmStorage : public CommandingServiceBase { + public: + enum Subservices : uint8_t { START_BY_TIME_RANGE_RETRIEVAL = 9, DELETE_UP_TO = 11 }; + explicit Service15TmStorage(object_id_t objectId, uint16_t apid, uint8_t numParallelCommands, + uint16_t commandTimeoutSecs = 60, size_t queueDepth = 10); + + private: + ReturnValue_t isValidSubservice(uint8_t subservice) override; + ReturnValue_t getMessageQueueAndObject(uint8_t subservice, const uint8_t* tcData, + size_t tcDataLen, MessageQueueId_t* id, + object_id_t* objectId) override; + ReturnValue_t prepareCommand(CommandMessage* message, uint8_t subservice, const uint8_t* tcData, + size_t tcDataLen, uint32_t* state, object_id_t objectId) override; + ReturnValue_t handleReply(const CommandMessage* reply, Command_t previousCommand, uint32_t* state, + CommandMessage* optionalNextCommand, object_id_t objectId, + bool* isStep) override; +}; + +#endif /* MISSION_TMTC_SERVICE15TMSTORAGE_H_ */ diff --git a/mission/tmtc/TmFunnelBase.cpp b/mission/tmtc/TmFunnelBase.cpp index 51bf93a1..6f788fc7 100644 --- a/mission/tmtc/TmFunnelBase.cpp +++ b/mission/tmtc/TmFunnelBase.cpp @@ -1,10 +1,12 @@ #include "TmFunnelBase.h" +#include + #include "fsfw/ipc/QueueFactory.h" -TmFunnelBase::TmFunnelBase(object_id_t objectId, StorageManagerIF &tmStore, uint32_t tmMsgDepth) - : SystemObject(objectId), tmStore(tmStore) { - tmQueue = QueueFactory::instance()->createMessageQueue(tmMsgDepth); +TmFunnelBase::TmFunnelBase(FunnelCfg cfg) + : SystemObject(cfg.objectId), tmStore(cfg.tmStore), ipcStore(cfg.ipcStore) { + tmQueue = QueueFactory::instance()->createMessageQueue(cfg.tmMsgDepth); } TmFunnelBase::~TmFunnelBase() { QueueFactory::instance()->deleteMessageQueue(tmQueue); } @@ -18,3 +20,38 @@ void TmFunnelBase::addDestination(const char *name, const AcceptsTelemetryIF &do auto queueId = downlinkDestination.getReportReceptionQueue(vcid); destinations.emplace_back(name, queueId, vcid); } + +ReturnValue_t TmFunnelBase::sendPacketToDestinations(store_address_t origStoreId, + TmTcMessage &message, + const uint8_t *packetData, size_t size) { + ReturnValue_t result = returnvalue::OK; + for (unsigned int idx = 0; idx < destinations.size(); idx++) { + const auto &dest = destinations[idx]; + if (destinations.size() > 1) { + if (idx < destinations.size() - 1) { + // Create copy of data to ensure each TM recipient has its own copy. That way, we don't need + // to bother with send order and where the data is deleted. + store_address_t storeId; + result = tmStore.addData(&storeId, packetData, size); + if (result == returnvalue::OK) { + message.setStorageId(storeId); + } else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "PusTmFunnel::handlePacket: Store too full to create data copy" + << std::endl; +#endif + } + } else { + message.setStorageId(origStoreId); + } + } + result = tmQueue->sendMessage(dest.queueId, &message); + if (result != returnvalue::OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "PusTmFunnel::handlePacket: Error sending TM to downlink handler" << std::endl; +#endif + tmStore.deleteData(message.getStorageId()); + } + } + return result; +} diff --git a/mission/tmtc/TmFunnelBase.h b/mission/tmtc/TmFunnelBase.h index 408244dd..af65771f 100644 --- a/mission/tmtc/TmFunnelBase.h +++ b/mission/tmtc/TmFunnelBase.h @@ -3,21 +3,36 @@ #include #include +#include #include +#include #include class TmFunnelBase : public AcceptsTelemetryIF, public SystemObject { public: - TmFunnelBase(object_id_t objectId, StorageManagerIF& tmStore, uint32_t tmMsgDepth); + struct FunnelCfg { + FunnelCfg(object_id_t objId, StorageManagerIF& tmStore, StorageManagerIF& ipcStore, + uint32_t tmMsgDepth) + : objectId(objId), tmStore(tmStore), ipcStore(ipcStore), tmMsgDepth(tmMsgDepth) {} + object_id_t objectId; + StorageManagerIF& tmStore; + StorageManagerIF& ipcStore; + uint32_t tmMsgDepth; + }; + explicit TmFunnelBase(FunnelCfg cfg); void addDestination(const char* name, const AcceptsTelemetryIF& downlinkDestination, uint8_t vcid = 0); + ReturnValue_t sendPacketToDestinations(store_address_t origStoreId, TmTcMessage& message, + const uint8_t* packetData, size_t size); [[nodiscard]] MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel) const override; - virtual ~TmFunnelBase(); + ~TmFunnelBase() override; protected: StorageManagerIF& tmStore; + StorageManagerIF& ipcStore; + struct Destination { Destination(const char* name, MessageQueueId_t queueId, uint8_t virtualChannel) : name(name), queueId(queueId), virtualChannel(virtualChannel) {} diff --git a/mission/utility/CMakeLists.txt b/mission/utility/CMakeLists.txt index e2459ed1..dadce33f 100644 --- a/mission/utility/CMakeLists.txt +++ b/mission/utility/CMakeLists.txt @@ -1,3 +1,3 @@ target_sources( ${LIB_EIVE_MISSION} PRIVATE Timestamp.cpp ProgressPrinter.cpp Filenaming.cpp - GlobalConfigHandler.cpp) + GlobalConfigHandler.cpp DummySdCardManager.cpp) diff --git a/mission/utility/DummySdCardManager.cpp b/mission/utility/DummySdCardManager.cpp new file mode 100644 index 00000000..17d7ba2b --- /dev/null +++ b/mission/utility/DummySdCardManager.cpp @@ -0,0 +1,13 @@ +#include "DummySdCardManager.h" + +DummySdCardManager::DummySdCardManager(std::string prefix) : prefix(std::move(prefix)) {} + +const char* DummySdCardManager::getCurrentMountPrefix() const { return prefix.c_str(); } + +bool DummySdCardManager::isSdCardUsable(std::optional sdCard) { return true; } + +std::optional DummySdCardManager::getPreferredSdCard() const { return std::nullopt; } + +void DummySdCardManager::setActiveSdCard(sd::SdCard sdCard) {} + +std::optional DummySdCardManager::getActiveSdCard() const { return std::nullopt; } diff --git a/bsp_linux_board/RPiSdCardManager.h b/mission/utility/DummySdCardManager.h similarity index 65% rename from bsp_linux_board/RPiSdCardManager.h rename to mission/utility/DummySdCardManager.h index 068471c1..4c985056 100644 --- a/bsp_linux_board/RPiSdCardManager.h +++ b/mission/utility/DummySdCardManager.h @@ -2,11 +2,11 @@ #define BSP_LINUX_BOARD_RPISDCARDMANAGER_H_ #include -class RPiSdCardManager : public SdCardMountedIF { +class DummySdCardManager : public SdCardMountedIF { public: - RPiSdCardManager(std::string prefix); - const std::string& getCurrentMountPrefix() const override; - bool isSdCardUsable(sd::SdCard sdCard) override; + DummySdCardManager(std::string prefix); + const char* getCurrentMountPrefix() const override; + bool isSdCardUsable(std::optional sdCard) override; std::optional getPreferredSdCard() const override; void setActiveSdCard(sd::SdCard sdCard) override; std::optional getActiveSdCard() const override; diff --git a/release_checklist.md b/release_checklist.md index b4f08165..2e0247c7 100644 --- a/release_checklist.md +++ b/release_checklist.md @@ -4,8 +4,10 @@ OBSW Release Checklist # Pre-Release 1. Update version in `CMakeLists.txt` -2. Verify that the Q7S, Q7S EM and Host build are working -3. Wait for CI/CD results +2. Re-run the auto-formatters with the `scripts/auto-formatter.sh` script +3. Re-run the generators with `generators/gen.py all` +4. Verify that the Q7S, Q7S EM and Host build are working +5. Wait for CI/CD results # Post-Release diff --git a/tmtc b/tmtc index 24f0d8e1..9720fcdd 160000 --- a/tmtc +++ b/tmtc @@ -1 +1 @@ -Subproject commit 24f0d8e1a6a8ea1323623932e699326214c78159 +Subproject commit 9720fcddecb04b228dc5eb0d064f15a12ef8daca diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 2ff542fd..20b98979 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(mocks) target_sources(${UNITTEST_NAME} PRIVATE main.cpp testEnvironment.cpp + testStampInFilename.cpp hdlcEncodingRw.cpp printChar.cpp ) \ No newline at end of file diff --git a/unittest/testStampInFilename.cpp b/unittest/testStampInFilename.cpp new file mode 100644 index 00000000..c66bdce6 --- /dev/null +++ b/unittest/testStampInFilename.cpp @@ -0,0 +1,21 @@ + +#include +#include + +#include "fsfw/timemanager/Clock.h" + +TEST_CASE("Stamp in Filename", "[Stamp In Filename]") { + Clock::TimeOfDay_t tod; + std::string baseName = "verif"; + std::string pathStr = "verif_2022-05-25T16:55:23Z.bin"; + unsigned int underscorePos = pathStr.find_last_of('_'); + std::string stampStr = pathStr.substr(underscorePos + 1); + float seconds = 0.0; + char* prefix = nullptr; + int count = + sscanf(stampStr.c_str(), + "%4" SCNu32 "-%2" SCNu32 "-%2" SCNu32 "T%2" SCNu32 ":%2" SCNu32 ":%2" SCNu32 "Z", + &tod.year, &tod.month, &tod.day, &tod.hour, &tod.minute, &tod.second); + static_cast(count); + CHECK(count == 6); +}