diff --git a/.idea/cmake.xml b/.idea/cmake.xml index 3be3a2b0..eff02c30 100644 --- a/.idea/cmake.xml +++ b/.idea/cmake.xml @@ -2,7 +2,7 @@ - + @@ -10,6 +10,7 @@ + \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 34e7fa34..3f151a03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,9 +16,277 @@ will consitute of a breaking change warranting a new major release: # [unreleased] -# [v4.0.0] to be released +## Changed -# [v3.1.0] to be released +- Relaxed SUS FDIR. The devices have shown to be glitchy in orbit, but still seem to deliver + sensible raw values most of the time. Some further testing is necessary, but some changes in the + code should cause the SUS devices to remain healthy for now. + +# [v6.3.0] 2023-08-03 + +## Fixed + +- Small SCEX fix: The temperatur check option was not passed + on for commands with a user data size larger than 1. +- SCEX: Properly check whether filesystem is usable for filesystem checks. +- ACS Controller strategy is now actually written to the dataset for detumbling. +- During detumble the fused rotation rate is now calculated. +- Detumbling is now exited when its exit value is undercut and not its entry value. +- Rotation rate of last cycle is now stored in all cases for the fused rotational rate + calculation. +- Fused rotation rate estimation during eclipse can be disabled. This will also prevent + detumbling during eclipse, as no relevant rotational rate is available for now. +- `EiveSystem`: Add a small delay between triggering an event for FDIR reboots and sending the + command to the core controller. +- PL PDU: Fixed bounds checking logic. Bound checks will only be performed for modules which are + enabled. + +## Changed + +- SCEX: Only perform filesystem checks when not in OFF mode. +- The `EiveSystem` now only sends reboot commands targetting the same image. +- Added 200 ms delay between switching HPA/MPA/TX/X8 and DRO GPIO pin OFF. +- PL PCDU ADC set is now automatically enabled for `NORMAL` mode transitions. It is automatically + disabled for `OFF` mode transitions. + +## Added + +- PL PCDU for EM build. +- SCEX: Add warning event if filesystem is unusable. + +# [v6.2.0] 2023-07-26 + +- `eive-tmtc`: v5.3.1 + +## Changed + +- STR missed reply handling is now moved to DHB rather than the COM interface. The COM IF will + still trigger an event if a reply is taking too long, and FDIR should still work via reply + timeouts. +- Re-ordered some functions of the core controller in the initialize function. +- Rad sensor is now only polled every 30 minutes instead of every device cycle to reduce wear of + the RADFET electronics. +- The SD cards will still be switched OFF on a reboot, but this is done in a non-blocking manner + now with a timeout of 10 seconds where the reboot will be performed in any case. +- ACS Controller now includes the safe mode from FLP, which will calculate its rotational rate + from SUS and MGM measurements. To accommodate these changes, low-pass filters for SUS + measurements and rates as well as MGM measurements and rates are included. Usage of the new + controller as well as settings of the low-pass filters can be handled via parameter commands. +- Simplify and fix the chip and copy protection functions in the core controller. This mechanism + now is always performed for the target chip and target copy in the reboot handlers. +- Improvement in FSFW: HK generation is now countdown based. + +## Added + +- 5 ms delay after pulling RADFET enable pin high before starting + the ADC conversion. +- Set STR time in configuration sequence to firmware mode. +- The STR `AutoThreshold` parameters are now set from the configuration JSON file at STR + startup. +- The STR handler can now handle the COM error reply and triggers an low severity event accordingly. +- Add SCEX handler for EM. +- Radiation sensor handler dummy for the EM. +- Added event for SD card information in core controller initialize function. This event will also + be triggered after the SD state machine has run, so the event will generally be triggered twice + at system boot, and once after commanding SD card switches. + +## Fixed + +- General bugs in the SD card state machine. This might fix some other known bugs for certain + combinations of switching ON and OFF SD cards and also makes the whole state machine a lot more + robust against hanging up. +- SUS dummy handler went to `MODE_NORMAL` for ON commands. +- PL PCDU dummy went to `MODE_NORMAL` for ON commands. + +# [v6.1.0] 2023-07-13 + +- `eive-tmtc`: v5.2.0 + +## Changed + +- TCS: Remove OBC IF board thermal module, which is exactly identical to OBC module and therefore + obsolete. +- Swapped PL and PS I2C, BPX battery and MGT are connected to PS I2C now for firmware versions + equal or above v4. However, this software version is compatible to both v3 and v4 of the firmware. +- The firmware version variables are global statics inititalized early during the program + runtime now. This makes it possible to check the firmware version earlier. +- The TCS controller will now always command heaters OFF when being blind for thermal + components (no sensors available), irrespective of current switch state. +- Make OBSW compatible to prospective FW version v5.0.0, where the Q7 I2C devices were + moved to a PL I2C block and the TMP sensor devices were moved to the PS I2C0. +- Made `Xadc` code a little bit more robust against errors. + +## Fixed + +- TMP1075: Set dataset invalid on shutdown explicitely +- Small fixes for TMP1075 FDIR: Use strange and missed reply counters. +- TCS controller: Last heater (S-band heater) was skipped for transition completion + checks. +- TCS controller: A helper member to track the elapsed heater control cycles was not reset + properly, which could lead to switch transitions being completed immediately. This can + lead to weird bugs like heaters being commanded ON twice and can potentially lead to + other bugs. +- TMP1075: Devices did not go to OFF mode when being set faulty. +- Update PL PCDU 1 in TCS mode tree on the EM. +- TMP1075: Possibly ignored health commands. +- Bugfix in FSFW where certain packet types could only be sent with source data fields with a + maximum size of 255 bytes. +- TCS CTRL: Limit number of heater handler messages sent in case there are not sensors available + anymore. +- Fix to allow adding real STR device for EM. + +# Added + +- Two events for heaters being commanded ON and OFF by the TCS controller +- Upper limit for burn time of TCS heaters. Currently set to 1 hour for each heater. + This mechanism will only track the burn time for heaters which were commanded by the + TCS controller. +- TCS controller is now observable by introducing a new HK dataset which exposes some internal + fields related to TCS control. + +# [v6.0.0] 2023-07-02 + +- `q7s-package` version v3.2.0 +- Important bugfixes for PTME. See `q7s-package` CHANGELOG. + +## Changed + +- Added back PTME busy bit polling. This is necessary due to changes to the AXI APB interface + to the PTME core. + +## Fixed + +- For the live channel (VC0), telemetry was still only dumped if the transmitter is active. + Please note that this fix will lead to crashes for FW versions below v3.2. + However, it might not be an issue for the oldest firmware on the satellite (v2.5.1). + +# [v5.2.0] 2023-07-02 + +## Fixed + +- The firmware information event was not triggered even when possible because of an ordering + bug in the initializer function. +- Empty dumps (no TM in time range) will now correctly be completed immediately + +## Changed + +- PTME was always reset on submode changes. The reset will now only be performed if the actual data + rate changes. +- Add back ACS board code for the EM. Now that the radiation sensor was removed, the image switching + issue has disappeared and adding back the ACS board is worth it for the GPS timekeeping. + +# [v5.1.0] 2023-06-28 + +- `eive-tmtc` version v5.1.0 + +## Changed + +- Persistent TM store dumps are now performed in chronological order. +- Increase Syrlinks RX HK rate to 5.0 seconds during a pass. +- Various robustness improvements for the heater handler. The heater handler will now only + process the command queue if it is not busy with switch commanding which reduces the amount + of possible bugs. +- The heater handler is only able to process messages stricly sequentially now but is scheduled + twice in a 0.5 second slot so something like a consecutive heater ON or OFF command can still + be handled relatively quickly. + +## Added + +- Sequence counters for PUS and CFDP packets are now stored persistently across graceful reboots. +- The PUS packet message type counter will now be incremented properly for each PUS service. +- Internal error reporter set is now enabled by default and generated every 120 seconds. + +# [v5.0.0] 2023-06-26 + +v3.3.1 and all following version will now be moved to v5.0.0 with the additional changes listed +here. This was done because the firmware update (v4.0.0) is not working right now and it is not +known when and how it will be fixed. Because of that, all updates to make the SW work with the new +firmware, which are limited to a few files will be moved to a dev branch so regular development +compatible to the old firmware can continue. + +TLDR: This version is compatible to the old firmware and some changes which only work with the new +firmware have been reverted. + +## Changed + +- Added `sync` syscall in graceful shutdown handler +- Graceful shutdown is now performed by the reboot watchdog +- There is now a separate file for the total reboot counter. The reboot watchdog has its own local + counters to determine whether a reboot is necessary. + +# [v4.0.1] 2023-06-24 + +## Fixed + +- `PusLiveDemux` packet demultiplexing bugfix where the demultiplexing did not work when there was + only one destination available. + +# [v4.0.0] 2023-06-22 + +- `eive-tmtc` version v5.0.0 +- `q7s-package` version v3.1.1 + +## Fixed + +- Important bugfixes for PTME. See `q7s-package` CHANGELOG. +- TCS fixes: Set temperature values to invalid value for MAX31865 RTD handler, SUS handler + and STR handler. Also set dataset to invakid for RTD handler. +- Fixed H parameter in SUS converter from 1 mm to 2.5 mm. + +## Changed + +- Removed PTME busy/ready signals. Those were not used anyway because register reads are used now. +- APB bus access busy checking is not done anymore as this is performed by the bus itself now. +- Core controller will now announce version and image information event in addition to reboot + event in the `inititalize` function. +- Core controller will now try to request and announce the firmware version in addition to the + OBSW version as well. +- Added core controller action to read reboot mechansm information +- GNSS reset pin will now only be asserted for 5 ms instead of 100 ms. + +## Added + +- Added PL I2C reset pin. It is not used/connected for now but could be used for FDIR procedures to + restore the PL I2C. +- Core controller now announces firmware version as well when requesting a version info event + +# [v3.3.1] 2023-06-22 + +## Fixed + +- `PusLiveDemux` packet demultiplexing bugfix where the demultiplexing did not work when there was + only one destination available. + +## Fixed + +- Fixed H parameter in SUS converter from 1 mm to 2.5 mm. + +# [v3.3.0] 2023-06-21 + +Like v3.2.0 but without the custom FM changes related to VC0. + +# [v3.2.0] 2023-06-21 + +## Fixed + +- Fix sun vector calculation +- SUS total vector was not reset to being a zero vector during eclipse due to a wrong memcpy + length. + +## Changed + +- Reverted all changes related to VC0 handling to avoid FM bug possibly related to FPGA bug. + +# [v3.1.1] 2023-06-14 + +## Fixed + +- TMP1075 bugfix where negative temperatures could not be measured because of a two's-complement + conversion bug. + +# [v3.1.0] 2023-06-14 + +- `eive-tmtc` version v4.1.0 ## Fixed @@ -27,11 +295,12 @@ will consitute of a breaking change warranting a new major release: ## Changed -- Relaxed SUS FDIR. The devices have shown to be glitchy in orbit, but still seem to deliver - sensible raw values most of the time. Some further testing is necessary, but some changes in the - code should cause the SUS devices to remain healthy for now. - Increase number of allowed parallel HK commands to 16 +## Added + +- Added `CONFIG_SET`, `MAN_HEATER_ON` and `MAN_HEATER_OFF` support for the BPX battery handler + # [v3.0.0] 2023-06-11 - `eive-tmtc` version v4.0.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8256dc27..908c89ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,8 @@ # ############################################################################## cmake_minimum_required(VERSION 3.13) -set(OBSW_VERSION_MAJOR 3) -set(OBSW_VERSION_MINOR 0) +set(OBSW_VERSION_MAJOR 6) +set(OBSW_VERSION_MINOR 3) set(OBSW_VERSION_REVISION 0) # set(CMAKE_VERBOSE TRUE) @@ -93,7 +93,7 @@ set(OBSW_ADD_BPX_BATTERY_HANDLER 1 CACHE STRING "Add BPX battery module") set(OBSW_ADD_STAR_TRACKER - ${INIT_VAL} + 1 CACHE STRING "Add Startracker module") set(OBSW_ADD_SUN_SENSORS ${INIT_VAL} @@ -105,7 +105,7 @@ set(OBSW_ADD_THERMAL_TEMP_INSERTER ${OBSW_Q7S_EM} CACHE STRING "Add thermal sensor temperature inserter") set(OBSW_ADD_ACS_BOARD - ${INIT_VAL} + 1 CACHE STRING "Add ACS board module") set(OBSW_ADD_GPS_CTRL ${INIT_VAL} @@ -144,13 +144,13 @@ set(OBSW_ADD_RAD_SENSORS ${INIT_VAL} CACHE STRING "Add Rad Sensor module") set(OBSW_ADD_PL_PCDU - ${INIT_VAL} + 1 CACHE STRING "Add Payload PCDU modukle") set(OBSW_ADD_SYRLINKS 1 CACHE STRING "Add Syrlinks module") set(OBSW_ADD_TMP_DEVICES - ${INIT_VAL} + 1 CACHE STRING "Add TMP devices") set(OBSW_ADD_GOMSPACE_PCDU 1 @@ -162,7 +162,7 @@ set(OBSW_ADD_RW ${INIT_VAL} CACHE STRING "Add RW modules") set(OBSW_ADD_SCEX_DEVICE - ${INIT_VAL} + 1 CACHE STRING "Add Solar Cell Experiment module") set(OBSW_SYRLINKS_SIMULATED 0 diff --git a/README.md b/README.md index 6b06283c..ce57c33f 100644 --- a/README.md +++ b/README.md @@ -964,6 +964,12 @@ used by other software components to read the current chip and copy. This is a configuration scripts which runs after the Network Time Protocol has run. This script currently sets the static IP address `192.168.133.10` and starts the `can` interface. +## Initial boot delay + +You can create a file named `boot_delays_secs.txt` inside the home folder to delay the OBSW boot +for 6 seconds if the file is empty of for the number of seconds specified inside the file. This +can be helpful if something inside the OBSW leads to an immediate crash of the OBC. + ## PCDU Connect to serial console of P60 Dock diff --git a/bsp_hosted/ObjectFactory.cpp b/bsp_hosted/ObjectFactory.cpp index dd88d552..4873bc58 100644 --- a/bsp_hosted/ObjectFactory.cpp +++ b/bsp_hosted/ObjectFactory.cpp @@ -68,7 +68,7 @@ void ObjectFactory::produce(void* args) { #endif auto sdcMan = new DummySdCardManager("/tmp"); ObjectFactory::produceGenericObjects(nullptr, &pusFunnel, &cfdpFunnel, *sdcMan, &ipcStore, - &tmStore, persistentStores, 120); + &tmStore, persistentStores, 120, enableHkSets); new TmFunnelHandler(objects::LIVE_TM_TASK, *pusFunnel, *cfdpFunnel); auto* dummyGpioIF = new DummyGpioIF(); @@ -113,7 +113,7 @@ void ObjectFactory::produce(void* args) { if (heaterHandler == nullptr) { sif::error << "HeaterHandler could not be created" << std::endl; } else { - ObjectFactory::createThermalController(*heaterHandler); + ObjectFactory::createThermalController(*heaterHandler, true); } new TestTask(objects::TEST_TASK); } diff --git a/bsp_hosted/fsfwconfig/events/translateEvents.cpp b/bsp_hosted/fsfwconfig/events/translateEvents.cpp index 79a828f2..98c5035f 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 296 translations. + * @brief Auto-generated event translation file. Contains 303 translations. * @details - * Generated on: 2023-06-13 07:54:32 + * Generated on: 2023-08-03 13:36:37 */ #include "translateEvents.h" @@ -139,6 +139,7 @@ const char *ERROR_STATE_STRING = "ERROR_STATE"; const char *RESET_OCCURED_STRING = "RESET_OCCURED"; const char *BOOTING_FIRMWARE_FAILED_EVENT_STRING = "BOOTING_FIRMWARE_FAILED_EVENT"; const char *BOOTING_BOOTLOADER_FAILED_EVENT_STRING = "BOOTING_BOOTLOADER_FAILED_EVENT"; +const char *COM_ERROR_REPLY_RECEIVED_STRING = "COM_ERROR_REPLY_RECEIVED"; const char *SUPV_MEMORY_READ_RPT_CRC_FAILURE_STRING = "SUPV_MEMORY_READ_RPT_CRC_FAILURE"; const char *SUPV_UNKNOWN_TM_STRING = "SUPV_UNKNOWN_TM"; const char *SUPV_UNINIMPLEMENTED_TM_STRING = "SUPV_UNINIMPLEMENTED_TM"; @@ -260,6 +261,7 @@ const char *TX_OFF_STRING = "TX_OFF"; const char *MISSING_PACKET_STRING = "MISSING_PACKET"; const char *EXPERIMENT_TIMEDOUT_STRING = "EXPERIMENT_TIMEDOUT"; const char *MULTI_PACKET_COMMAND_DONE_STRING = "MULTI_PACKET_COMMAND_DONE"; +const char *FS_UNUSABLE_STRING = "FS_UNUSABLE"; const char *SET_CONFIGFILEVALUE_FAILED_STRING = "SET_CONFIGFILEVALUE_FAILED"; const char *GET_CONFIGFILEVALUE_FAILED_STRING = "GET_CONFIGFILEVALUE_FAILED"; const char *INSERT_CONFIGFILEVALUE_FAILED_STRING = "INSERT_CONFIGFILEVALUE_FAILED"; @@ -277,6 +279,8 @@ const char *INDIVIDUAL_BOOT_COUNTS_STRING = "INDIVIDUAL_BOOT_COUNTS"; const char *TRYING_I2C_RECOVERY_STRING = "TRYING_I2C_RECOVERY"; const char *I2C_REBOOT_STRING = "I2C_REBOOT"; const char *PDEC_REBOOT_STRING = "PDEC_REBOOT"; +const char *FIRMWARE_INFO_STRING = "FIRMWARE_INFO"; +const char *ACTIVE_SD_INFO_STRING = "ACTIVE_SD_INFO"; 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"; @@ -285,6 +289,9 @@ const char *CAMERA_OVERHEATING_STRING = "CAMERA_OVERHEATING"; const char *PCDU_SYSTEM_OVERHEATING_STRING = "PCDU_SYSTEM_OVERHEATING"; const char *HEATER_NOT_OFF_FOR_OFF_MODE_STRING = "HEATER_NOT_OFF_FOR_OFF_MODE"; const char *MGT_OVERHEATING_STRING = "MGT_OVERHEATING"; +const char *TCS_SWITCHING_HEATER_ON_STRING = "TCS_SWITCHING_HEATER_ON"; +const char *TCS_SWITCHING_HEATER_OFF_STRING = "TCS_SWITCHING_HEATER_OFF"; +const char *TCS_HEATER_MAX_BURN_TIME_REACHED_STRING = "TCS_HEATER_MAX_BURN_TIME_REACHED"; const char *TX_TIMER_EXPIRED_STRING = "TX_TIMER_EXPIRED"; const char *BIT_LOCK_TX_ON_STRING = "BIT_LOCK_TX_ON"; const char *POSSIBLE_FILE_CORRUPTION_STRING = "POSSIBLE_FILE_CORRUPTION"; @@ -572,6 +579,8 @@ const char *translateEvents(Event event) { return BOOTING_FIRMWARE_FAILED_EVENT_STRING; case (11902): return BOOTING_BOOTLOADER_FAILED_EVENT_STRING; + case (11903): + return COM_ERROR_REPLY_RECEIVED_STRING; case (12001): return SUPV_MEMORY_READ_RPT_CRC_FAILURE_STRING; case (12002): @@ -814,6 +823,8 @@ const char *translateEvents(Event event) { return EXPERIMENT_TIMEDOUT_STRING; case (13802): return MULTI_PACKET_COMMAND_DONE_STRING; + case (13803): + return FS_UNUSABLE_STRING; case (13901): return SET_CONFIGFILEVALUE_FAILED_STRING; case (13902): @@ -848,6 +859,10 @@ const char *translateEvents(Event event) { return I2C_REBOOT_STRING; case (14012): return PDEC_REBOOT_STRING; + case (14013): + return FIRMWARE_INFO_STRING; + case (14014): + return ACTIVE_SD_INFO_STRING; case (14100): return NO_VALID_SENSOR_TEMPERATURE_STRING; case (14101): @@ -864,6 +879,12 @@ const char *translateEvents(Event event) { return HEATER_NOT_OFF_FOR_OFF_MODE_STRING; case (14108): return MGT_OVERHEATING_STRING; + case (14109): + return TCS_SWITCHING_HEATER_ON_STRING; + case (14110): + return TCS_SWITCHING_HEATER_OFF_STRING; + case (14111): + return TCS_HEATER_MAX_BURN_TIME_REACHED_STRING; case (14201): return TX_TIMER_EXPIRED_STRING; case (14202): diff --git a/bsp_hosted/fsfwconfig/objects/translateObjects.cpp b/bsp_hosted/fsfwconfig/objects/translateObjects.cpp index 68b13d3b..bd5ef90f 100644 --- a/bsp_hosted/fsfwconfig/objects/translateObjects.cpp +++ b/bsp_hosted/fsfwconfig/objects/translateObjects.cpp @@ -2,7 +2,7 @@ * @brief Auto-generated object translation file. * @details * Contains 171 translations. - * Generated on: 2023-06-13 07:54:32 + * Generated on: 2023-08-03 13:36:37 */ #include "translateObjects.h" diff --git a/bsp_q7s/CMakeLists.txt b/bsp_q7s/CMakeLists.txt index cf8fcacd..e3232363 100644 --- a/bsp_q7s/CMakeLists.txt +++ b/bsp_q7s/CMakeLists.txt @@ -7,7 +7,8 @@ target_link_libraries(${SIMPLE_OBSW_NAME} PUBLIC ${LIB_FSFW_NAME}) target_compile_definitions(${SIMPLE_OBSW_NAME} PRIVATE "Q7S_SIMPLE_MODE") add_subdirectory(simple) -target_sources(${OBSW_NAME} PUBLIC main.cpp obsw.cpp) +target_sources(${OBSW_NAME} PUBLIC main.cpp obsw.cpp scheduling.cpp + objectFactory.cpp) add_subdirectory(boardtest) diff --git a/bsp_q7s/boardconfig/busConf.h b/bsp_q7s/boardconfig/busConf.h index 4fd15258..dc3779a7 100644 --- a/bsp_q7s/boardconfig/busConf.h +++ b/bsp_q7s/boardconfig/busConf.h @@ -12,6 +12,9 @@ static constexpr char SPI_RW_DEV[] = "/dev/spi_rw"; static constexpr char I2C_PL_EIVE[] = "/dev/i2c_pl"; //! I2C bus using the I2C peripheral of the ARM processing system (PS) static constexpr char I2C_PS_EIVE[] = "/dev/i2c_ps"; +//! I2C bus using the first I2C peripheral of the ARM processing system (PS). +//! Named like this because it is used by default for the Q7 devices. +static constexpr char I2C_Q7_EIVE[] = "/dev/i2c_q7"; static constexpr char UART_GNSS_DEV[] = "/dev/gps0"; static constexpr char UART_PLOC_MPSOC_DEV[] = "/dev/ul_plmpsoc"; @@ -23,6 +26,7 @@ static constexpr char UART_SCEX_DEV[] = "/dev/scex"; static constexpr char UIO_PDEC_REGISTERS[] = "/dev/uio_pdec_regs"; static constexpr char UIO_PTME[] = "/dev/uio_ptme"; static constexpr char UIO_PDEC_CONFIG_MEMORY[] = "/dev/uio_pdec_cfg_mem"; +static constexpr char UIO_SYS_ROM[] = "/dev/uio_sys_rom"; static constexpr char UIO_PDEC_RAM[] = "/dev/uio_pdec_ram"; static constexpr char UIO_PDEC_IRQ[] = "/dev/uio_pdec_irq"; static constexpr int MAP_ID_PTME_CONFIG = 3; @@ -57,6 +61,7 @@ static constexpr char GYRO_0_ENABLE[] = "enable_gyro_0"; static constexpr char GYRO_2_ENABLE[] = "enable_gyro_2"; static constexpr char GNSS_SELECT[] = "gnss_mux_select"; static constexpr char GNSS_MUX_SELECT[] = "gnss_mux_select"; +static constexpr char PL_I2C_ARESETN[] = "pl_i2c_aresetn"; static constexpr char HEATER_0[] = "heater0"; static constexpr char HEATER_1[] = "heater1"; @@ -82,14 +87,12 @@ static constexpr char EN_RW_4[] = "enable_rw_4"; static constexpr char RAD_SENSOR_CHIP_SELECT[] = "rad_sensor_chip_select"; static constexpr char ENABLE_RADFET[] = "enable_radfet"; -static constexpr char PAPB_BUSY_SIGNAL_VC0[] = "papb_busy_signal_vc0"; + static constexpr char PAPB_EMPTY_SIGNAL_VC0[] = "papb_empty_signal_vc0"; -static constexpr char PAPB_BUSY_SIGNAL_VC1[] = "papb_busy_signal_vc1"; static constexpr char PAPB_EMPTY_SIGNAL_VC1[] = "papb_empty_signal_vc1"; -static constexpr char PAPB_BUSY_SIGNAL_VC2[] = "papb_busy_signal_vc2"; static constexpr char PAPB_EMPTY_SIGNAL_VC2[] = "papb_empty_signal_vc2"; -static constexpr char PAPB_BUSY_SIGNAL_VC3[] = "papb_busy_signal_vc3"; static constexpr char PAPB_EMPTY_SIGNAL_VC3[] = "papb_empty_signal_vc3"; + static constexpr char PTME_RESETN[] = "ptme_resetn"; static constexpr char RS485_EN_TX_CLOCK[] = "tx_clock_enable_ltc2872"; diff --git a/bsp_q7s/boardtest/Q7STestTask.cpp b/bsp_q7s/boardtest/Q7STestTask.cpp index 50a34284..03805fde 100644 --- a/bsp_q7s/boardtest/Q7STestTask.cpp +++ b/bsp_q7s/boardtest/Q7STestTask.cpp @@ -218,15 +218,30 @@ void Q7STestTask::testProtHandler() { bool opPerformed = false; ReturnValue_t result = returnvalue::OK; // If any chips are unlocked, lock them here - result = coreController->setBootCopyProtection(xsc::Chip::ALL_CHIP, xsc::Copy::ALL_COPY, true, - opPerformed, true); + result = coreController->setBootCopyProtectionAndUpdateFile(xsc::Chip::CHIP_0, xsc::Copy::COPY_0, + true); + if (result != returnvalue::OK) { + sif::warning << "Q7STestTask::testProtHandler: Op failed" << std::endl; + } + result = coreController->setBootCopyProtectionAndUpdateFile(xsc::Chip::CHIP_0, xsc::Copy::COPY_1, + true); + if (result != returnvalue::OK) { + sif::warning << "Q7STestTask::testProtHandler: Op failed" << std::endl; + } + result = coreController->setBootCopyProtectionAndUpdateFile(xsc::Chip::CHIP_1, xsc::Copy::COPY_0, + true); + if (result != returnvalue::OK) { + sif::warning << "Q7STestTask::testProtHandler: Op failed" << std::endl; + } + result = coreController->setBootCopyProtectionAndUpdateFile(xsc::Chip::CHIP_1, xsc::Copy::COPY_1, + true); if (result != returnvalue::OK) { sif::warning << "Q7STestTask::testProtHandler: Op failed" << std::endl; } // unlock own copy - result = coreController->setBootCopyProtection(xsc::Chip::SELF_CHIP, xsc::Copy::SELF_COPY, false, - opPerformed, true); + result = coreController->setBootCopyProtectionAndUpdateFile(xsc::Chip::SELF_CHIP, + xsc::Copy::SELF_COPY, false); if (result != returnvalue::OK) { sif::warning << "Q7STestTask::testProtHandler: Op failed" << std::endl; } @@ -239,8 +254,8 @@ void Q7STestTask::testProtHandler() { } // lock own copy - result = coreController->setBootCopyProtection(xsc::Chip::SELF_CHIP, xsc::Copy::SELF_COPY, true, - opPerformed, true); + result = coreController->setBootCopyProtectionAndUpdateFile(xsc::Chip::SELF_CHIP, + xsc::Copy::SELF_COPY, true); if (result != returnvalue::OK) { sif::warning << "Q7STestTask::testProtHandler: Op failed" << std::endl; } @@ -253,8 +268,8 @@ void Q7STestTask::testProtHandler() { } // unlock specific copy - result = coreController->setBootCopyProtection(xsc::Chip::CHIP_1, xsc::Copy::COPY_1, false, - opPerformed, true); + result = coreController->setBootCopyProtectionAndUpdateFile(xsc::Chip::CHIP_1, xsc::Copy::COPY_1, + false); if (result != returnvalue::OK) { sif::warning << "Q7STestTask::testProtHandler: Op failed" << std::endl; } @@ -267,8 +282,8 @@ void Q7STestTask::testProtHandler() { } // lock specific copy - result = coreController->setBootCopyProtection(xsc::Chip::CHIP_1, xsc::Copy::COPY_1, true, - opPerformed, true); + result = coreController->setBootCopyProtectionAndUpdateFile(xsc::Chip::CHIP_1, xsc::Copy::COPY_1, + true); if (result != returnvalue::OK) { sif::warning << "Q7STestTask::testProtHandler: Op failed" << std::endl; } diff --git a/bsp_q7s/core/CMakeLists.txt b/bsp_q7s/core/CMakeLists.txt index b726885b..33550144 100644 --- a/bsp_q7s/core/CMakeLists.txt +++ b/bsp_q7s/core/CMakeLists.txt @@ -1,4 +1 @@ -target_sources(${OBSW_NAME} PRIVATE CoreController.cpp scheduling.cpp - ObjectFactory.cpp WatchdogHandler.cpp) - -target_sources(${SIMPLE_OBSW_NAME} PRIVATE scheduling.cpp) +target_sources(${OBSW_NAME} PRIVATE CoreController.cpp WatchdogHandler.cpp) diff --git a/bsp_q7s/core/CoreController.cpp b/bsp_q7s/core/CoreController.cpp index 76460aa5..e1b48fd2 100644 --- a/bsp_q7s/core/CoreController.cpp +++ b/bsp_q7s/core/CoreController.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "commonConfig.h" #include "fsfw/serviceinterface/ServiceInterface.h" @@ -22,6 +23,7 @@ #include #include +#include "bsp_q7s/boardconfig/busConf.h" #include "bsp_q7s/fs/SdCardManager.h" #include "bsp_q7s/memory/scratchApi.h" #include "bsp_q7s/xadc/Xadc.h" @@ -154,21 +156,12 @@ ReturnValue_t CoreController::initialize() { if (result != returnvalue::OK) { sif::warning << "CoreController::initialize: Base init failed" << std::endl; } - result = scratch::writeNumber(scratch::ALLOC_FAILURE_COUNT, 0); - if (result != returnvalue::OK) { - sif::warning << "CoreController::initialize: Setting up alloc failure " - "count failed" - << std::endl; - } result = paramHelper.initialize(); if (result != returnvalue::OK) { return result; } - sdStateMachine(); - - triggerEvent(core::REBOOT_SW, CURRENT_CHIP, CURRENT_COPY); EventManagerIF *eventManager = ObjectManager::instance()->get(objects::EVENT_MANAGER); if (eventManager == nullptr or eventQueue == nullptr) { @@ -185,7 +178,20 @@ ReturnValue_t CoreController::initialize() { if (result != returnvalue::OK) { sif::warning << "Subscribing for GPS GPS_FIX_CHANGE event failed" << std::endl; } - return returnvalue::OK; + triggerEvent(core::REBOOT_SW, CURRENT_CHIP, CURRENT_COPY); + announceCurrentImageInfo(); + announceVersionInfo(); + SdCardManager::SdStatePair sdStates; + sdcMan->getSdCardsStatus(sdStates); + announceSdInfo(sdStates); + sdStateMachine(); + result = scratch::writeNumber(scratch::ALLOC_FAILURE_COUNT, 0); + if (result != returnvalue::OK) { + sif::warning << "CoreController::initialize: Setting up alloc failure " + "count failed" + << std::endl; + } + return result; } ReturnValue_t CoreController::initializeAfterTaskCreation() { @@ -210,19 +216,7 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_ using namespace core; switch (actionId) { case (ANNOUNCE_VERSION): { - uint32_t p1 = (common::OBSW_VERSION_MAJOR << 24) | (common::OBSW_VERSION_MINOR << 16) | - (common::OBSW_VERSION_REVISION << 8); - uint32_t p2 = 0; - if (strcmp("", common::OBSW_VERSION_CST_GIT_SHA1) != 0) { - p1 |= 1; - auto shaAsStr = std::string(common::OBSW_VERSION_CST_GIT_SHA1); - size_t posDash = shaAsStr.find("-"); - auto gitHash = shaAsStr.substr(posDash + 2, 4); - // Only copy first 4 letters of git hash - memcpy(&p2, gitHash.c_str(), 4); - } - - triggerEvent(VERSION_INFO, p1, p2); + announceVersionInfo(); return HasActionsIF::EXECUTION_FINISHED; } case (ANNOUNCE_BOOT_COUNTS): { @@ -230,7 +224,7 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_ return HasActionsIF::EXECUTION_FINISHED; } case (ANNOUNCE_CURRENT_IMAGE): { - triggerEvent(CURRENT_IMAGE_INFO, CURRENT_CHIP, CURRENT_COPY); + announceCurrentImageInfo(); return HasActionsIF::EXECUTION_FINISHED; } case (LIST_DIRECTORY_INTO_FILE): { @@ -246,6 +240,9 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_ return result; } std::ostringstream oss("cp ", std::ostringstream::ate); + if (parser.isForceOptSet()) { + oss << "-f "; + } if (parser.isRecursiveOptSet()) { oss << "-r "; } @@ -318,28 +315,38 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_ if (size < 1) { return HasActionsIF::INVALID_PARAMETERS; } - std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_FILE; - // Disable the reboot file mechanism - parseRebootFile(path, rebootFile); + std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_WATCHDOG_FILE; + parseRebootWatchdogFile(path, rebootWatchdogFile); if (data[0] == 0) { - rebootFile.enabled = false; - rewriteRebootFile(rebootFile); + rebootWatchdogFile.enabled = false; + rewriteRebootWatchdogFile(rebootWatchdogFile); } else if (data[0] == 1) { - rebootFile.enabled = true; - rewriteRebootFile(rebootFile); + rebootWatchdogFile.enabled = true; + rewriteRebootWatchdogFile(rebootWatchdogFile); } else { return HasActionsIF::INVALID_PARAMETERS; } return HasActionsIF::EXECUTION_FINISHED; } + case (READ_REBOOT_MECHANISM_INFO): { + std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_WATCHDOG_FILE; + parseRebootWatchdogFile(path, rebootWatchdogFile); + RebootWatchdogPacket packet(rebootWatchdogFile); + ReturnValue_t result = actionHelper.reportData(commandedBy, actionId, &packet); + if (result != returnvalue::OK) { + return result; + } + return HasActionsIF::EXECUTION_FINISHED; + } case (RESET_REBOOT_COUNTERS): { if (size == 0) { - resetRebootCount(xsc::ALL_CHIP, xsc::ALL_COPY); + resetRebootWatchdogCounters(xsc::ALL_CHIP, xsc::ALL_COPY); } else if (size == 2) { if (data[0] > 1 or data[1] > 1) { return HasActionsIF::INVALID_PARAMETERS; } - resetRebootCount(static_cast(data[0]), static_cast(data[1])); + resetRebootWatchdogCounters(static_cast(data[0]), + static_cast(data[1])); } return HasActionsIF::EXECUTION_FINISHED; } @@ -434,11 +441,11 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_ if (size < 1) { return HasActionsIF::INVALID_PARAMETERS; } - std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_FILE; + std::string path = sdcMan->getCurrentMountPrefix() + REBOOT_WATCHDOG_FILE; // Disable the reboot file mechanism - parseRebootFile(path, rebootFile); - rebootFile.maxCount = data[0]; - rewriteRebootFile(rebootFile); + parseRebootWatchdogFile(path, rebootWatchdogFile); + rebootWatchdogFile.maxCount = data[0]; + rewriteRebootWatchdogFile(rebootWatchdogFile); return HasActionsIF::EXECUTION_FINISHED; } case (XSC_REBOOT_OBC): { @@ -547,21 +554,24 @@ ReturnValue_t CoreController::sdStateMachine() { } // This lambda checks the non-blocking operation of the SD card manager and assigns the new - // state on success. It returns true for an operation success and false otherwise + // state on success. It returns 0 for an operation success, -1 for failed operations, and 1 + // for pending operations auto nonBlockingSdcOpChecking = [&](SdStates newStateOnSuccess, uint16_t maxCycleCount, std::string opPrintout) { SdCardManager::OpStatus status = sdcMan->checkCurrentOp(operation); - if (status == SdCardManager::OpStatus::SUCCESS) { + if (status == SdCardManager::OpStatus::SUCCESS or sdInfo.cycleCount > maxCycleCount) { sdFsmState = newStateOnSuccess; sdInfo.commandPending = false; + if (sdInfo.cycleCount > maxCycleCount) { + sif::warning << "CoreController::sdStateMachine: " << opPrintout << " takes too long" + << std::endl; + sdInfo.cycleCount = 0; + return -1; + } sdInfo.cycleCount = 0; - return true; - } else if (sdInfo.cycleCount > 4) { - sif::warning << "CoreController::sdStateMachine: " << opPrintout << " takes too long" - << std::endl; - return false; - } - return false; + return 0; + }; + return 1; }; if (sdFsmState == SdStates::UPDATE_SD_INFO_START) { @@ -637,7 +647,7 @@ ReturnValue_t CoreController::sdStateMachine() { sdFsmState = tgtState; } } else { - if (nonBlockingSdcOpChecking(SdStates::MOUNT_SELF, 10, "Setting SDC state")) { + if (nonBlockingSdcOpChecking(SdStates::MOUNT_SELF, 10, "Setting SDC state") <= 0) { sdInfo.activeState = sd::SdState::ON; currentStateSetter(sdInfo.active, sd::SdState::ON); // Skip the two cycles now. @@ -665,7 +675,7 @@ ReturnValue_t CoreController::sdStateMachine() { result = sdCardSetup(sdInfo.active, sd::SdState::MOUNTED, sdInfo.activeChar); sdInfo.commandPending = true; } else { - if (nonBlockingSdcOpChecking(SdStates::DETERMINE_OTHER, 5, "Mounting SD card")) { + if (nonBlockingSdcOpChecking(SdStates::DETERMINE_OTHER, 5, "Mounting SD card") <= 0) { sdcMan->setActiveSdCard(sdInfo.active); currMntPrefix = sdcMan->getCurrentMountPrefix(); sdInfo.activeState = sd::SdState::MOUNTED; @@ -707,12 +717,7 @@ ReturnValue_t CoreController::sdStateMachine() { sdInfo.commandPending = true; } else { if (nonBlockingSdcOpChecking(SdStates::SKIP_CYCLE_BEFORE_INFO_UPDATE, 10, - "Switching off other SD card")) { - sdInfo.otherState = sd::SdState::OFF; - currentStateSetter(sdInfo.other, sd::SdState::OFF); - } else { - // Continue.. avoid being stuck here.. - sdFsmState = SdStates::SKIP_CYCLE_BEFORE_INFO_UPDATE; + "Switching off other SD card") <= 0) { sdInfo.otherState = sd::SdState::OFF; currentStateSetter(sdInfo.other, sd::SdState::OFF); } @@ -723,12 +728,7 @@ ReturnValue_t CoreController::sdStateMachine() { sdInfo.commandPending = true; } else { if (nonBlockingSdcOpChecking(SdStates::MOUNT_UNMOUNT_OTHER, 10, - "Switching on other SD card")) { - sdInfo.otherState = sd::SdState::ON; - currentStateSetter(sdInfo.other, sd::SdState::ON); - } else { - // Contnue.. avoid being stuck here. - sdFsmState = SdStates::MOUNT_UNMOUNT_OTHER; + "Switching on other SD card") <= 0) { sdInfo.otherState = sd::SdState::ON; currentStateSetter(sdInfo.other, sd::SdState::ON); } @@ -743,7 +743,8 @@ ReturnValue_t CoreController::sdStateMachine() { result = sdCardSetup(sdInfo.other, sd::SdState::ON, sdInfo.otherChar); sdInfo.commandPending = true; } else { - if (nonBlockingSdcOpChecking(SdStates::SET_STATE_OTHER, 10, "Unmounting other SD card")) { + if (nonBlockingSdcOpChecking(SdStates::SET_STATE_OTHER, 10, "Unmounting other SD card") <= + 0) { sdInfo.otherState = sd::SdState::ON; currentStateSetter(sdInfo.other, sd::SdState::ON); } else { @@ -757,7 +758,8 @@ ReturnValue_t CoreController::sdStateMachine() { result = sdCardSetup(sdInfo.other, sd::SdState::MOUNTED, sdInfo.otherChar); sdInfo.commandPending = true; } else { - if (nonBlockingSdcOpChecking(SdStates::UPDATE_SD_INFO_END, 4, "Mounting other SD card")) { + if (nonBlockingSdcOpChecking(SdStates::UPDATE_SD_INFO_END, 4, "Mounting other SD card") <= + 0) { sdInfo.otherState = sd::SdState::MOUNTED; currentStateSetter(sdInfo.other, sd::SdState::MOUNTED); } @@ -794,6 +796,7 @@ ReturnValue_t CoreController::sdStateMachine() { } sif::info << "SD card update into " << modeStr << " mode finished. Active SD: " << sdInfo.activeChar << std::endl; + announceSdInfo(sdInfo.currentState); if (not sdInfo.initFinished) { updateInternalSdInfo(); sdInfo.initFinished = true; @@ -832,7 +835,7 @@ ReturnValue_t CoreController::sdCardSetup(sd::SdCard sdCard, sd::SdState targetS if (state == sd::SdState::MOUNTED) { if (targetState == sd::SdState::OFF) { sif::info << "Switching off SD card " << sdChar << std::endl; - return sdcMan->switchOffSdCard(sdCard, true, &sdInfo.currentState); + return sdcMan->switchOffSdCard(sdCard, sdInfo.currentState, true); } else if (targetState == sd::SdState::ON) { sif::info << "Unmounting SD card " << sdChar << std::endl; return sdcMan->unmountSdCard(sdCard); @@ -866,7 +869,7 @@ ReturnValue_t CoreController::sdCardSetup(sd::SdCard sdCard, sd::SdState targetS return sdcMan->mountSdCard(sdCard); } else if (targetState == sd::SdState::OFF) { sif::info << "Switching off SD card " << sdChar << std::endl; - return sdcMan->switchOffSdCard(sdCard, false, &sdInfo.currentState); + return sdcMan->switchOffSdCard(sdCard, sdInfo.currentState, false); } } else { sif::warning << "CoreController::sdCardSetup: Invalid state for this call" << std::endl; @@ -890,8 +893,7 @@ ReturnValue_t CoreController::sdColdRedundantBlockingInit() { sif::info << "Switching off secondary SD card " << sdInfo.otherChar << std::endl; // Switch off other SD card in cold redundant mode if setting up preferred one worked // without issues - ReturnValue_t result2 = - sdcMan->switchOffSdCard(sdInfo.other, sdInfo.otherState, &sdInfo.currentState); + ReturnValue_t result2 = sdcMan->switchOffSdCard(sdInfo.other, sdInfo.currentState, true); if (result2 != returnvalue::OK and result2 != SdCardManager::ALREADY_OFF) { sif::warning << "Switching off secondary SD card " << sdInfo.otherChar << " in cold redundant mode failed" << std::endl; @@ -1196,45 +1198,7 @@ ReturnValue_t CoreController::actionXscReboot(const uint8_t *data, size_t size) auto tgtChip = static_cast(data[1]); auto tgtCopy = static_cast(data[2]); - // This function can not really fail - gracefulShutdownTasks(tgtChip, tgtCopy, protOpPerformed); - - switch (tgtChip) { - case (xsc::Chip::CHIP_0): { - switch (tgtCopy) { - case (xsc::Copy::COPY_0): { - xsc_boot_copy(XSC_LIBNOR_CHIP_0, XSC_LIBNOR_COPY_NOMINAL); - break; - } - case (xsc::Copy::COPY_1): { - xsc_boot_copy(XSC_LIBNOR_CHIP_0, XSC_LIBNOR_COPY_GOLD); - break; - } - default: { - break; - } - } - break; - } - case (xsc::Chip::CHIP_1): { - switch (tgtCopy) { - case (xsc::Copy::COPY_0): { - xsc_boot_copy(XSC_LIBNOR_CHIP_1, XSC_LIBNOR_COPY_NOMINAL); - break; - } - case (xsc::Copy::COPY_1): { - xsc_boot_copy(XSC_LIBNOR_CHIP_1, XSC_LIBNOR_COPY_GOLD); - break; - } - default: { - break; - } - } - break; - } - default: - break; - } + performGracefulShutdown(tgtChip, tgtCopy); return returnvalue::FAILED; } @@ -1247,21 +1211,37 @@ ReturnValue_t CoreController::actionReboot(const uint8_t *data, size_t size) { ReturnValue_t CoreController::gracefulShutdownTasks(xsc::Chip chip, xsc::Copy copy, bool &protOpPerformed) { + // Store both sequence counters persistently. + core::SAVE_CFDP_SEQUENCE_COUNT = true; + core::SAVE_PUS_SEQUENCE_COUNT = true; + sdcMan->setBlocking(true); sdcMan->markUnusable(); // Wait two seconds to ensure no one uses the SD cards TaskFactory::delayTask(2000); - // Attempt graceful shutdown by unmounting and switching off SD cards - sdcMan->switchOffSdCard(sd::SdCard::SLOT_0); - sdcMan->switchOffSdCard(sd::SdCard::SLOT_1); - // If any boot copies are unprotected - ReturnValue_t result = setBootCopyProtection(xsc::Chip::SELF_CHIP, xsc::Copy::SELF_COPY, true, - protOpPerformed, false); - if (result == returnvalue::OK and protOpPerformed) { + + // Ensure that all writes/reads do finish. + sync(); + + // Unmount and switch off SD cards. This could possibly fix issues with the SD card and is + // the more graceful way to reboot the system. This function takes around 400 ms. + ReturnValue_t result = handleSwitchingSdCardsOffNonBlocking(); + if (result != returnvalue::OK) { + sif::error + << "CoreController::gracefulShutdownTasks: Issues unmounting or switching SD cards off" + << std::endl; + } + + // Ensure that the target chip is writeprotected in any case. + bool wasProtected = handleBootCopyProt(chip, copy, true); + if (wasProtected) { // TODO: Would be nice to notify operator. But we can't use the filesystem anymore // and a reboot is imminent. Use scratch buffer? sif::info << "Running slot was writeprotected before reboot" << std::endl; } + sif::info << "Graceful shutdown handling done" << std::endl; + // Ensure that all diagnostic prinouts arrive. + TaskFactory::delayTask(50); return result; } @@ -1295,144 +1275,50 @@ ReturnValue_t CoreController::generateChipStateFile() { return returnvalue::OK; } -ReturnValue_t CoreController::setBootCopyProtection(xsc::Chip targetChip, xsc::Copy targetCopy, - bool protect, bool &protOperationPerformed, - bool updateProtFile) { - bool allChips = false; - bool allCopies = false; - bool selfChip = false; - bool selfCopy = false; - protOperationPerformed = false; - - switch (targetChip) { - case (xsc::Chip::ALL_CHIP): { - allChips = true; - break; - } - case (xsc::Chip::NO_CHIP): { - return returnvalue::OK; - } - case (xsc::Chip::SELF_CHIP): { - selfChip = true; - targetChip = CURRENT_CHIP; - break; - } - default: { - break; - } - } - switch (targetCopy) { - case (xsc::Copy::ALL_COPY): { - allCopies = true; - break; - } - case (xsc::Copy::NO_COPY): { - return returnvalue::OK; - } - case (xsc::Copy::SELF_COPY): { - selfCopy = true; - targetCopy = CURRENT_COPY; - break; - } - default: { - break; - } +ReturnValue_t CoreController::setBootCopyProtectionAndUpdateFile(xsc::Chip targetChip, + xsc::Copy targetCopy, + bool protect) { + if (targetChip == xsc::Chip::ALL_CHIP or targetCopy == xsc::Copy::ALL_COPY) { + return returnvalue::FAILED; } - for (uint8_t arrIdx = 0; arrIdx < protArray.size(); arrIdx++) { - int result = handleBootCopyProtAtIndex(targetChip, targetCopy, protect, protOperationPerformed, - selfChip, selfCopy, allChips, allCopies, arrIdx); - if (result != 0) { - break; - } - } - if (protOperationPerformed and updateProtFile) { + bool protOperationPerformed = handleBootCopyProt(targetChip, targetCopy, protect); + if (protOperationPerformed) { updateProtInfo(); } return returnvalue::OK; } -int CoreController::handleBootCopyProtAtIndex(xsc::Chip targetChip, xsc::Copy targetCopy, - bool protect, bool &protOperationPerformed, - bool selfChip, bool selfCopy, bool allChips, - bool allCopies, uint8_t arrIdx) { - bool currentProt = protArray[arrIdx]; +bool CoreController::handleBootCopyProt(xsc::Chip targetChip, xsc::Copy targetCopy, bool protect) { std::ostringstream oss; - bool performOp = false; - if (protect == currentProt) { - return 0; - } - if (protOperationPerformed) { - if ((selfChip and selfCopy) or (not allCopies and not allChips)) { - // No need to continue, only one operation was requested - return 1; - } - } - xsc::Chip currentChip; - xsc::Copy currentCopy; oss << "writeprotect "; - if (arrIdx == 0 or arrIdx == 1) { - oss << "0 "; - currentChip = xsc::Chip::CHIP_0; - } else { - oss << "1 "; - currentChip = xsc::Chip::CHIP_1; + if (targetChip == xsc::Chip::SELF_CHIP) { + targetChip = CURRENT_CHIP; } - if (arrIdx == 0 or arrIdx == 2) { + if (targetCopy == xsc::Copy::SELF_COPY) { + targetCopy = CURRENT_COPY; + } + if (targetChip == xsc::Chip::CHIP_0) { oss << "0 "; - currentCopy = xsc::Copy::COPY_0; - } else { + } else if (targetChip == xsc::Chip::CHIP_1) { + oss << "1 "; + } + if (targetCopy == xsc::Copy::COPY_0) { + oss << "0 "; + } else if (targetCopy == xsc::Copy::COPY_1) { oss << "1 "; - currentCopy = xsc::Copy::COPY_1; } if (protect) { oss << "1"; } else { oss << "0"; } - - int result = 0; - if (allChips and allCopies) { - performOp = true; - } else if (allChips) { - if ((selfCopy and CURRENT_COPY == targetCopy) or (currentCopy == targetCopy)) { - performOp = true; - } - } else if (allCopies) { - if ((selfChip and CURRENT_COPY == targetCopy) or (currentChip == targetChip)) { - performOp = true; - } - } else if (selfChip and (currentChip == targetChip)) { - if (selfCopy) { - if (currentCopy == targetCopy) { - performOp = true; - } - } else { - performOp = true; - } - - } else if (selfCopy and (currentCopy == targetCopy)) { - if (selfChip) { - if (currentChip == targetChip) { - performOp = true; - } - } else { - performOp = true; - } - } else if ((targetChip == currentChip) and (targetCopy == currentCopy)) { - performOp = true; + sif::info << "Executing command: " << oss.str() << std::endl; + int result = std::system(oss.str().c_str()); + if (result == 0) { + return true; } - if (result != 0) { - utility::handleSystemError(result, "CoreController::checkAndSetBootCopyProtection"); - } - if (performOp) { - // TODO: Lock operation take a long time. Use command executor? That would require a - // new state machine.. - protOperationPerformed = true; - sif::info << "Executing command: " << oss.str() << std::endl; - result = std::system(oss.str().c_str()); - } - return 0; + return false; } ReturnValue_t CoreController::updateProtInfo(bool regenerateChipStateFile) { @@ -1477,7 +1363,6 @@ ReturnValue_t CoreController::handleProtInfoUpdateLine(std::string nextLine) { using namespace std; string word; uint8_t wordIdx = 0; - uint8_t arrayIdx = 0; istringstream iss(nextLine); xsc::Chip currentChip = xsc::Chip::CHIP_0; xsc::Copy currentCopy = xsc::Copy::COPY_0; @@ -1489,28 +1374,11 @@ ReturnValue_t CoreController::handleProtInfoUpdateLine(std::string nextLine) { currentCopy = static_cast(stoi(word)); } - if (wordIdx == 3) { - if (currentChip == xsc::Chip::CHIP_0) { - if (currentCopy == xsc::Copy::COPY_0) { - arrayIdx = 0; - } else if (currentCopy == xsc::Copy::COPY_1) { - arrayIdx = 1; - } - } - - else if (currentChip == xsc::Chip::CHIP_1) { - if (currentCopy == xsc::Copy::COPY_0) { - arrayIdx = 2; - } else if (currentCopy == xsc::Copy::COPY_1) { - arrayIdx = 3; - } - } - } if (wordIdx == 5) { if (word == "unlocked.") { - protArray[arrayIdx] = false; + protArray[currentChip][currentCopy] = false; } else { - protArray[arrayIdx] = true; + protArray[currentChip][currentCopy] = true; } } wordIdx++; @@ -1543,7 +1411,8 @@ void CoreController::performMountedSdCardOperations() { if (not timeFileInitDone) { initClockFromTimeFile(); } - performRebootFileHandling(false); + performRebootWatchdogHandling(false); + performRebootCountersHandling(false); } backupTimeFileHandler(); }; @@ -1615,118 +1484,127 @@ ReturnValue_t CoreController::performSdCardCheck() { return returnvalue::OK; } -void CoreController::performRebootFileHandling(bool recreateFile) { +void CoreController::performRebootWatchdogHandling(bool recreateFile) { using namespace std; - std::string path = currMntPrefix + REBOOT_FILE; + std::string path = currMntPrefix + REBOOT_WATCHDOG_FILE; + std::string legacyPath = currMntPrefix + LEGACY_REBOOT_WATCHDOG_FILE; std::error_code e; + // TODO: Remove at some point in the future. + if (std::filesystem::exists(legacyPath, e)) { + // Old file might still exist, so copy it to new path + std::filesystem::copy(legacyPath, path, std::filesystem::copy_options::overwrite_existing, e); + if (e) { + sif::error << "File copy has failed: " << e.message() << std::endl; + } + } if (not std::filesystem::exists(path, e) or recreateFile) { #if OBSW_VERBOSE_LEVEL >= 1 - sif::info << "CoreController::performRebootFileHandling: Recreating reboot file" << std::endl; + sif::info << "CoreController::performRebootFileHandling: Recreating reboot watchdog file" + << std::endl; #endif - rebootFile.enabled = false; - rebootFile.img00Cnt = 0; - rebootFile.img01Cnt = 0; - rebootFile.img10Cnt = 0; - rebootFile.img11Cnt = 0; - rebootFile.lastChip = xsc::Chip::CHIP_0; - rebootFile.lastCopy = xsc::Copy::COPY_0; - rebootFile.img00Lock = false; - rebootFile.img01Lock = false; - rebootFile.img10Lock = false; - rebootFile.img11Lock = false; - rebootFile.mechanismNextChip = xsc::Chip::NO_CHIP; - rebootFile.mechanismNextCopy = xsc::Copy::NO_COPY; - rebootFile.bootFlag = false; - rewriteRebootFile(rebootFile); + rebootWatchdogFile.enabled = false; + rebootWatchdogFile.img00Cnt = 0; + rebootWatchdogFile.img01Cnt = 0; + rebootWatchdogFile.img10Cnt = 0; + rebootWatchdogFile.img11Cnt = 0; + rebootWatchdogFile.lastChip = xsc::Chip::CHIP_0; + rebootWatchdogFile.lastCopy = xsc::Copy::COPY_0; + rebootWatchdogFile.img00Lock = false; + rebootWatchdogFile.img01Lock = false; + rebootWatchdogFile.img10Lock = false; + rebootWatchdogFile.img11Lock = false; + rebootWatchdogFile.mechanismNextChip = xsc::Chip::NO_CHIP; + rebootWatchdogFile.mechanismNextCopy = xsc::Copy::NO_COPY; + rebootWatchdogFile.bootFlag = false; + rewriteRebootWatchdogFile(rebootWatchdogFile); } else { - if (not parseRebootFile(path, rebootFile)) { - performRebootFileHandling(true); + if (not parseRebootWatchdogFile(path, rebootWatchdogFile)) { + performRebootWatchdogHandling(true); + return; } } if (CURRENT_CHIP == xsc::CHIP_0) { if (CURRENT_COPY == xsc::COPY_0) { - rebootFile.img00Cnt++; + rebootWatchdogFile.img00Cnt++; } else { - rebootFile.img01Cnt++; + rebootWatchdogFile.img01Cnt++; } } else { if (CURRENT_COPY == xsc::COPY_0) { - rebootFile.img10Cnt++; + rebootWatchdogFile.img10Cnt++; } else { - rebootFile.img11Cnt++; + rebootWatchdogFile.img11Cnt++; } } - if (rebootFile.bootFlag) { + if (rebootWatchdogFile.bootFlag) { // Trigger event to inform ground that a reboot was triggered - uint32_t p1 = rebootFile.lastChip << 16 | rebootFile.lastCopy; + uint32_t p1 = rebootWatchdogFile.lastChip << 16 | rebootWatchdogFile.lastCopy; triggerEvent(core::REBOOT_MECHANISM_TRIGGERED, p1, 0); // Clear the boot flag - rebootFile.bootFlag = false; + rebootWatchdogFile.bootFlag = false; } - announceBootCounts(); - - if (rebootFile.mechanismNextChip != xsc::NO_CHIP and - rebootFile.mechanismNextCopy != xsc::NO_COPY) { - if (CURRENT_CHIP != rebootFile.mechanismNextChip or - CURRENT_COPY != rebootFile.mechanismNextCopy) { - std::string infoString = std::to_string(rebootFile.mechanismNextChip) + " " + - std::to_string(rebootFile.mechanismNextCopy); + if (rebootWatchdogFile.mechanismNextChip != xsc::NO_CHIP and + rebootWatchdogFile.mechanismNextCopy != xsc::NO_COPY) { + if (CURRENT_CHIP != rebootWatchdogFile.mechanismNextChip or + CURRENT_COPY != rebootWatchdogFile.mechanismNextCopy) { + std::string infoString = std::to_string(rebootWatchdogFile.mechanismNextChip) + " " + + std::to_string(rebootWatchdogFile.mechanismNextCopy); sif::warning << "CoreController::performRebootFileHandling: Expected to be on image " << infoString << " but currently on other image. Locking " << infoString << std::endl; // Firmware or other component might be corrupt and we are on another image then the target // image specified by the mechanism. We can't really trust the target image anymore. // Lock it for now - if (rebootFile.mechanismNextChip == xsc::CHIP_0) { - if (rebootFile.mechanismNextCopy == xsc::COPY_0) { - rebootFile.img00Lock = true; + if (rebootWatchdogFile.mechanismNextChip == xsc::CHIP_0) { + if (rebootWatchdogFile.mechanismNextCopy == xsc::COPY_0) { + rebootWatchdogFile.img00Lock = true; } else { - rebootFile.img01Lock = true; + rebootWatchdogFile.img01Lock = true; } } else { - if (rebootFile.mechanismNextCopy == xsc::COPY_0) { - rebootFile.img10Lock = true; + if (rebootWatchdogFile.mechanismNextCopy == xsc::COPY_0) { + rebootWatchdogFile.img10Lock = true; } else { - rebootFile.img11Lock = true; + rebootWatchdogFile.img11Lock = true; } } } } - rebootFile.lastChip = CURRENT_CHIP; - rebootFile.lastCopy = CURRENT_COPY; + rebootWatchdogFile.lastChip = CURRENT_CHIP; + rebootWatchdogFile.lastCopy = CURRENT_COPY; // Only reboot if the reboot functionality is enabled. // The handler will still increment the boot counts - if (rebootFile.enabled and (*rebootFile.relevantBootCnt >= rebootFile.maxCount)) { + if (rebootWatchdogFile.enabled and + (*rebootWatchdogFile.relevantBootCnt >= rebootWatchdogFile.maxCount)) { // Reboot to other image bool doReboot = false; xsc::Chip tgtChip = xsc::NO_CHIP; xsc::Copy tgtCopy = xsc::NO_COPY; - determineAndExecuteReboot(rebootFile, doReboot, tgtChip, tgtCopy); + rebootWatchdogAlgorithm(rebootWatchdogFile, doReboot, tgtChip, tgtCopy); if (doReboot) { - rebootFile.bootFlag = true; + rebootWatchdogFile.bootFlag = true; #if OBSW_VERBOSE_LEVEL >= 1 sif::info << "Boot counter for image " << CURRENT_CHIP << " " << CURRENT_COPY << " too high. Rebooting to " << tgtChip << " " << tgtCopy << std::endl; #endif - rebootFile.mechanismNextChip = tgtChip; - rebootFile.mechanismNextCopy = tgtCopy; - rewriteRebootFile(rebootFile); - xsc_boot_copy(static_cast(tgtChip), - static_cast(tgtCopy)); + rebootWatchdogFile.mechanismNextChip = tgtChip; + rebootWatchdogFile.mechanismNextCopy = tgtCopy; + rewriteRebootWatchdogFile(rebootWatchdogFile); + performGracefulShutdown(tgtChip, tgtCopy); } } else { - rebootFile.mechanismNextChip = xsc::NO_CHIP; - rebootFile.mechanismNextCopy = xsc::NO_COPY; + rebootWatchdogFile.mechanismNextChip = xsc::NO_CHIP; + rebootWatchdogFile.mechanismNextCopy = xsc::NO_COPY; } - rewriteRebootFile(rebootFile); + rewriteRebootWatchdogFile(rebootWatchdogFile); } -void CoreController::determineAndExecuteReboot(RebootFile &rf, bool &needsReboot, - xsc::Chip &tgtChip, xsc::Copy &tgtCopy) { +void CoreController::rebootWatchdogAlgorithm(RebootWatchdogFile &rf, bool &needsReboot, + xsc::Chip &tgtChip, xsc::Copy &tgtCopy) { tgtChip = xsc::CHIP_0; tgtCopy = xsc::COPY_0; needsReboot = false; @@ -1814,7 +1692,7 @@ void CoreController::determineAndExecuteReboot(RebootFile &rf, bool &needsReboot } } -bool CoreController::parseRebootFile(std::string path, RebootFile &rf) { +bool CoreController::parseRebootWatchdogFile(std::string path, RebootWatchdogFile &rf) { using namespace std; std::string selfMatch; if (CURRENT_CHIP == xsc::CHIP_0) { @@ -1996,68 +1874,174 @@ bool CoreController::parseRebootFile(std::string path, RebootFile &rf) { return true; } -void CoreController::resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy) { - std::string path = currMntPrefix + REBOOT_FILE; - // Disable the reboot file mechanism - parseRebootFile(path, rebootFile); +bool CoreController::parseRebootCountersFile(std::string path, RebootCountersFile &rf) { + using namespace std; + ifstream file(path); + string word; + string line; + uint8_t lineIdx = 0; + while (std::getline(file, line)) { + istringstream iss(line); + switch (lineIdx) { + case 0: { + iss >> word; + if (word.find("img00:") == string::npos) { + return false; + } + iss >> rf.img00Cnt; + + break; + } + case 1: { + iss >> word; + if (word.find("img01:") == string::npos) { + return false; + } + iss >> rf.img01Cnt; + + break; + } + case 2: { + iss >> word; + if (word.find("img10:") == string::npos) { + return false; + } + iss >> rf.img10Cnt; + + break; + } + case 3: { + iss >> word; + if (word.find("img11:") == string::npos) { + return false; + } + iss >> rf.img11Cnt; + break; + } + } + lineIdx++; + } + return true; +} + +void CoreController::resetRebootWatchdogCounters(xsc::Chip tgtChip, xsc::Copy tgtCopy) { + std::string path = currMntPrefix + REBOOT_WATCHDOG_FILE; + parseRebootWatchdogFile(path, rebootWatchdogFile); if (tgtChip == xsc::ALL_CHIP and tgtCopy == xsc::ALL_COPY) { - rebootFile.img00Cnt = 0; - rebootFile.img01Cnt = 0; - rebootFile.img10Cnt = 0; - rebootFile.img11Cnt = 0; + rebootWatchdogFile.img00Cnt = 0; + rebootWatchdogFile.img01Cnt = 0; + rebootWatchdogFile.img10Cnt = 0; + rebootWatchdogFile.img11Cnt = 0; } else { if (tgtChip == xsc::CHIP_0) { if (tgtCopy == xsc::COPY_0) { - rebootFile.img00Cnt = 0; + rebootWatchdogFile.img00Cnt = 0; } else { - rebootFile.img01Cnt = 0; + rebootWatchdogFile.img01Cnt = 0; } } else { if (tgtCopy == xsc::COPY_0) { - rebootFile.img10Cnt = 0; + rebootWatchdogFile.img10Cnt = 0; } else { - rebootFile.img11Cnt = 0; + rebootWatchdogFile.img11Cnt = 0; } } } - rewriteRebootFile(rebootFile); + rewriteRebootWatchdogFile(rebootWatchdogFile); } -void CoreController::rewriteRebootFile(RebootFile file) { - std::string path = currMntPrefix + REBOOT_FILE; +void CoreController::performRebootCountersHandling(bool recreateFile) { + std::string path = currMntPrefix + REBOOT_COUNTERS_FILE; + std::error_code e; + if (not std::filesystem::exists(path, e) or recreateFile) { +#if OBSW_VERBOSE_LEVEL >= 1 + sif::info << "CoreController::performRebootFileHandling: Recreating reboot counters file" + << std::endl; +#endif + rebootCountersFile.img00Cnt = 0; + rebootCountersFile.img01Cnt = 0; + rebootCountersFile.img10Cnt = 0; + rebootCountersFile.img11Cnt = 0; + rewriteRebootCountersFile(rebootCountersFile); + } else { + if (not parseRebootCountersFile(path, rebootCountersFile)) { + performRebootCountersHandling(true); + return; + } + } + + if (CURRENT_CHIP == xsc::CHIP_0) { + if (CURRENT_COPY == xsc::COPY_0) { + rebootCountersFile.img00Cnt++; + } else { + rebootCountersFile.img01Cnt++; + } + } else { + if (CURRENT_COPY == xsc::COPY_0) { + rebootCountersFile.img10Cnt++; + } else { + rebootCountersFile.img11Cnt++; + } + } + announceBootCounts(); + rewriteRebootCountersFile(rebootCountersFile); +} +void CoreController::rewriteRebootWatchdogFile(RebootWatchdogFile file) { + using namespace std::filesystem; + std::string path = currMntPrefix + REBOOT_WATCHDOG_FILE; + std::string legacyPath = currMntPrefix + LEGACY_REBOOT_WATCHDOG_FILE; + { + std::ofstream rebootFile(path); + if (rebootFile.is_open()) { + // Initiate reboot file first. Reboot handling will be on on initialization + rebootFile << "on: " << file.enabled << "\nmaxcnt: " << file.maxCount + << "\nimg00: " << file.img00Cnt << "\nimg01: " << file.img01Cnt + << "\nimg10: " << file.img10Cnt << "\nimg11: " << file.img11Cnt + << "\nimg00lock: " << file.img00Lock << "\nimg01lock: " << file.img01Lock + << "\nimg10lock: " << file.img10Lock << "\nimg11lock: " << file.img11Lock + << "\nbootflag: " << file.bootFlag << "\nlast: " << static_cast(file.lastChip) + << " " << static_cast(file.lastCopy) + << "\nnext: " << static_cast(file.mechanismNextChip) << " " + << static_cast(file.mechanismNextCopy) << "\n"; + } + } + std::error_code e; + // TODO: Remove at some point in the future when all images have been updated. + if (std::filesystem::exists(legacyPath)) { + // Keep those two files in sync + std::filesystem::copy(path, legacyPath, std::filesystem::copy_options::overwrite_existing, e); + if (e) { + sif::error << "File copy has failed: " << e.message() << std::endl; + } + } +} + +void CoreController::rewriteRebootCountersFile(RebootCountersFile file) { + std::string path = currMntPrefix + REBOOT_COUNTERS_FILE; std::ofstream rebootFile(path); if (rebootFile.is_open()) { - // Initiate reboot file first. Reboot handling will be on on initialization - rebootFile << "on: " << file.enabled << "\nmaxcnt: " << file.maxCount - << "\nimg00: " << file.img00Cnt << "\nimg01: " << file.img01Cnt - << "\nimg10: " << file.img10Cnt << "\nimg11: " << file.img11Cnt - << "\nimg00lock: " << file.img00Lock << "\nimg01lock: " << file.img01Lock - << "\nimg10lock: " << file.img10Lock << "\nimg11lock: " << file.img11Lock - << "\nbootflag: " << file.bootFlag << "\nlast: " << static_cast(file.lastChip) - << " " << static_cast(file.lastCopy) - << "\nnext: " << static_cast(file.mechanismNextChip) << " " - << static_cast(file.mechanismNextCopy) << "\n"; + rebootFile << "img00: " << file.img00Cnt << "\nimg01: " << file.img01Cnt + << "\nimg10: " << file.img10Cnt << "\nimg11: " << file.img11Cnt << "\n"; } } void CoreController::setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::Copy tgtCopy) { - std::string path = currMntPrefix + REBOOT_FILE; - // Disable the reboot file mechanism - parseRebootFile(path, rebootFile); + std::string path = currMntPrefix + REBOOT_WATCHDOG_FILE; + parseRebootWatchdogFile(path, rebootWatchdogFile); if (tgtChip == xsc::CHIP_0) { if (tgtCopy == xsc::COPY_0) { - rebootFile.img00Lock = lock; + rebootWatchdogFile.img00Lock = lock; } else { - rebootFile.img01Lock = lock; + rebootWatchdogFile.img01Lock = lock; } } else { if (tgtCopy == xsc::COPY_0) { - rebootFile.img10Lock = lock; + rebootWatchdogFile.img10Lock = lock; } else { - rebootFile.img11Lock = lock; + rebootWatchdogFile.img11Lock = lock; } } - rewriteRebootFile(rebootFile); + rewriteRebootWatchdogFile(rebootWatchdogFile); } ReturnValue_t CoreController::backupTimeFileHandler() { @@ -2344,10 +2328,12 @@ bool CoreController::startSdStateMachine(sd::SdCard targetActiveSd, SdCfgMode mo } void CoreController::announceBootCounts() { - uint64_t totalBootCount = - rebootFile.img00Cnt + rebootFile.img01Cnt + rebootFile.img10Cnt + rebootFile.img11Cnt; - uint32_t individualBootCountsP1 = (rebootFile.img00Cnt << 16) | rebootFile.img01Cnt; - uint32_t individualBootCountsP2 = (rebootFile.img10Cnt << 16) | rebootFile.img11Cnt; + uint64_t totalBootCount = rebootCountersFile.img00Cnt + rebootCountersFile.img01Cnt + + rebootCountersFile.img10Cnt + rebootCountersFile.img11Cnt; + uint32_t individualBootCountsP1 = + (rebootCountersFile.img00Cnt << 16) | rebootCountersFile.img01Cnt; + uint32_t individualBootCountsP2 = + (rebootCountersFile.img10Cnt << 16) | rebootCountersFile.img11Cnt; triggerEvent(core::INDIVIDUAL_BOOT_COUNTS, individualBootCountsP1, individualBootCountsP2); triggerEvent(core::REBOOT_COUNTER, (totalBootCount >> 32) & 0xffffffff, totalBootCount & 0xffffffff); @@ -2405,6 +2391,138 @@ void CoreController::dirListingDumpHandler() { } } +void CoreController::announceVersionInfo() { + using namespace core; + uint32_t p1 = (common::OBSW_VERSION_MAJOR << 24) | (common::OBSW_VERSION_MINOR << 16) | + (common::OBSW_VERSION_REVISION << 8); + uint32_t p2 = 0; + if (strcmp("", common::OBSW_VERSION_CST_GIT_SHA1) != 0) { + p1 |= 1; + auto shaAsStr = std::string(common::OBSW_VERSION_CST_GIT_SHA1); + size_t posDash = shaAsStr.find("-"); + auto gitHash = shaAsStr.substr(posDash + 2, 4); + // Only copy first 4 letters of git hash + memcpy(&p2, gitHash.c_str(), 4); + } + + triggerEvent(VERSION_INFO, p1, p2); + p1 = (core::FW_VERSION_MAJOR << 24) | (core::FW_VERSION_MINOR << 16) | + (core::FW_VERSION_REVISION << 8) | (core::FW_VERSION_HAS_SHA); + std::memcpy(&p2, core::FW_VERSION_GIT_SHA, 4); + triggerEvent(FIRMWARE_INFO, p1, p2); +} + +void CoreController::announceCurrentImageInfo() { + using namespace core; + triggerEvent(CURRENT_IMAGE_INFO, CURRENT_CHIP, CURRENT_COPY); +} + +ReturnValue_t CoreController::performGracefulShutdown(xsc::Chip tgtChip, xsc::Copy tgtCopy) { + bool protOpPerformed = false; + // This function can not really fail + gracefulShutdownTasks(tgtChip, tgtCopy, protOpPerformed); + + switch (tgtChip) { + case (xsc::Chip::CHIP_0): { + switch (tgtCopy) { + case (xsc::Copy::COPY_0): { + xsc_boot_copy(XSC_LIBNOR_CHIP_0, XSC_LIBNOR_COPY_NOMINAL); + break; + } + case (xsc::Copy::COPY_1): { + xsc_boot_copy(XSC_LIBNOR_CHIP_0, XSC_LIBNOR_COPY_GOLD); + break; + } + default: { + break; + } + } + break; + } + case (xsc::Chip::CHIP_1): { + switch (tgtCopy) { + case (xsc::Copy::COPY_0): { + xsc_boot_copy(XSC_LIBNOR_CHIP_1, XSC_LIBNOR_COPY_NOMINAL); + break; + } + case (xsc::Copy::COPY_1): { + xsc_boot_copy(XSC_LIBNOR_CHIP_1, XSC_LIBNOR_COPY_GOLD); + break; + } + default: { + break; + } + } + break; + } + default: + break; + } + return returnvalue::OK; +} + +void CoreController::announceSdInfo(SdCardManager::SdStatePair sdStates) { + auto activeSdCard = sdcMan->getActiveSdCard(); + uint32_t p1 = sd::SdCard::NONE; + if (activeSdCard.has_value()) { + p1 = static_cast(activeSdCard.value()); + } + uint32_t p2 = + (static_cast(sdStates.first) << 16) | static_cast(sdStates.second); + triggerEvent(core::ACTIVE_SD_INFO, p1, p2); +} + +ReturnValue_t CoreController::handleSwitchingSdCardsOffNonBlocking() { + sdcMan->setBlocking(false); + SdCardManager::Operations op; + std::pair sdStatus; + ReturnValue_t result = sdcMan->getSdCardsStatus(sdStatus); + if (result != returnvalue::OK) { + return result; + } + Countdown maxWaitTimeCd(10000); + // Stopwatch watch; + auto waitingForFinish = [&]() { + auto currentState = sdcMan->checkCurrentOp(op); + if (currentState == SdCardManager::OpStatus::IDLE) { + return returnvalue::OK; + } + while (currentState == SdCardManager::OpStatus::ONGOING) { + if (maxWaitTimeCd.hasTimedOut()) { + return returnvalue::FAILED; + } + TaskFactory::delayTask(50); + currentState = sdcMan->checkCurrentOp(op); + } + return returnvalue::OK; + }; + if (sdStatus.first != sd::SdState::OFF) { + sdcMan->unmountSdCard(sd::SdCard::SLOT_0); + result = waitingForFinish(); + if (result != returnvalue::OK) { + return result; + } + sdcMan->switchOffSdCard(sd::SdCard::SLOT_0, sdStatus, false); + result = waitingForFinish(); + if (result != returnvalue::OK) { + return result; + } + } + if (sdStatus.second != sd::SdState::OFF) { + sdcMan->unmountSdCard(sd::SdCard::SLOT_1); + result = waitingForFinish(); + if (result != returnvalue::OK) { + return result; + } + sdcMan->switchOffSdCard(sd::SdCard::SLOT_1, sdStatus, false); + result = waitingForFinish(); + if (result != returnvalue::OK) { + return result; + } + } + return result; +} + bool CoreController::isNumber(const std::string &s) { return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end(); diff --git a/bsp_q7s/core/CoreController.h b/bsp_q7s/core/CoreController.h index 05878d6d..79224ed2 100644 --- a/bsp_q7s/core/CoreController.h +++ b/bsp_q7s/core/CoreController.h @@ -1,11 +1,13 @@ #ifndef BSP_Q7S_CORE_CORECONTROLLER_H_ #define BSP_Q7S_CORE_CORECONTROLLER_H_ +#include #include #include #include #include #include +#include #include #include #include @@ -13,7 +15,6 @@ #include #include -#include "CoreDefinitions.h" #include "OBSWConfig.h" #include "bsp_q7s/fs/SdCardManager.h" #include "events/subsystemIdRanges.h" @@ -23,7 +24,7 @@ class Timer; class SdCardManager; -struct RebootFile { +struct RebootWatchdogFile { static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10; bool enabled = true; @@ -44,6 +45,93 @@ struct RebootFile { xsc::Copy mechanismNextCopy = xsc::Copy::NO_COPY; }; +class RebootWatchdogPacket : public SerialLinkedListAdapter { + public: + RebootWatchdogPacket(RebootWatchdogFile& rf) { + enabled = rf.enabled; + maxCount = rf.maxCount; + img00Count = rf.img00Cnt; + img01Count = rf.img01Cnt; + img10Count = rf.img10Cnt; + img11Count = rf.img11Cnt; + img00Lock = rf.img00Lock; + img01Lock = rf.img01Lock; + img10Lock = rf.img10Lock; + img11Lock = rf.img11Lock; + lastChip = static_cast(rf.lastChip); + lastCopy = static_cast(rf.lastCopy); + nextChip = static_cast(rf.mechanismNextChip); + nextCopy = static_cast(rf.mechanismNextCopy); + setLinks(); + } + + private: + void setLinks() { + setStart(&enabled); + enabled.setNext(&maxCount); + maxCount.setNext(&img00Count); + img00Count.setNext(&img01Count); + img01Count.setNext(&img10Count); + img10Count.setNext(&img11Count); + img11Count.setNext(&img00Lock); + img00Lock.setNext(&img01Lock); + img01Lock.setNext(&img10Lock); + img10Lock.setNext(&img11Lock); + img11Lock.setNext(&lastChip); + lastChip.setNext(&lastCopy); + lastCopy.setNext(&nextChip); + nextChip.setNext(&nextCopy); + setLast(&nextCopy); + } + + SerializeElement enabled = false; + SerializeElement maxCount = 0; + SerializeElement img00Count = 0; + SerializeElement img01Count = 0; + SerializeElement img10Count = 0; + SerializeElement img11Count = 0; + SerializeElement img00Lock = false; + SerializeElement img01Lock = false; + SerializeElement img10Lock = false; + SerializeElement img11Lock = false; + SerializeElement lastChip = 0; + SerializeElement lastCopy = 0; + SerializeElement nextChip = 0; + SerializeElement nextCopy = 0; +}; + +struct RebootCountersFile { + // 16 bit values so all boot counters fit into one event. + uint16_t img00Cnt = 0; + uint16_t img01Cnt = 0; + uint16_t img10Cnt = 0; + uint16_t img11Cnt = 0; +}; + +class RebootCountersPacket : public SerialLinkedListAdapter { + RebootCountersPacket(RebootCountersFile& rf) { + img00Count = rf.img00Cnt; + img01Count = rf.img01Cnt; + img10Count = rf.img10Cnt; + img11Count = rf.img11Cnt; + setLinks(); + } + + private: + void setLinks() { + setStart(&img00Count); + img00Count.setNext(&img01Count); + img01Count.setNext(&img10Count); + img10Count.setNext(&img11Count); + setLast(&img11Count); + } + + SerializeElement img00Count = 0; + SerializeElement img01Count = 0; + SerializeElement img10Count = 0; + SerializeElement img11Count = 0; +}; + class CoreController : public ExtendedControllerBase, public ReceivesParameterMessagesIF { public: enum ParamId : uint8_t { PREF_SD = 0, NUM_IDS }; @@ -57,10 +145,15 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe const std::string VERSION_FILE = "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::VERSION_FILE_NAME); - const std::string REBOOT_FILE = - "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::REBOOT_FILE_NAME); + const std::string LEGACY_REBOOT_WATCHDOG_FILE = + "/" + std::string(core::CONF_FOLDER) + "/" + + std::string(core::LEGACY_REBOOT_WATCHDOG_FILE_NAME); + const std::string REBOOT_WATCHDOG_FILE = + "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::REBOOT_WATCHDOG_FILE_NAME); const std::string BACKUP_TIME_FILE = "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::TIME_FILE_NAME); + const std::string REBOOT_COUNTERS_FILE = + "/" + std::string(core::CONF_FOLDER) + "/" + std::string(core::REBOOT_COUNTER_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"; @@ -106,8 +199,8 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe * @param updateProtFile Specify whether the protection info file is updated * @return */ - ReturnValue_t setBootCopyProtection(xsc::Chip targetChip, xsc::Copy targetCopy, bool protect, - bool& protOperationPerformed, bool updateProtFile = true); + ReturnValue_t setBootCopyProtectionAndUpdateFile(xsc::Chip targetChip, xsc::Copy targetCopy, + bool protect); bool sdInitFinished() const; @@ -142,6 +235,7 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe static constexpr bool BLOCKING_SD_INIT = false; + uint32_t* mappedSysRomAddr = nullptr; SdCardManager* sdcMan = nullptr; MessageQueueIF* eventQueue = nullptr; @@ -191,7 +285,8 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe std::array dirListingBuf{}; DirListingDumpContext dumpContext{}; - RebootFile rebootFile = {}; + RebootWatchdogFile rebootWatchdogFile = {}; + RebootCountersFile rebootCountersFile = {}; CommandExecutor cmdExecutor; SimpleRingBuffer cmdReplyBuf; @@ -209,12 +304,10 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe Countdown sdCardCheckCd = Countdown(INIT_SD_CARD_CHECK_TIMEOUT); /** - * Index 0: Chip 0 Copy 0 - * Index 1: Chip 0 Copy 1 - * Index 2: Chip 1 Copy 0 - * Index 3: Chip 1 Copy 1 + * First index: Chip. + * Second index: Copy. */ - std::array protArray; + bool protArray[2][2]{}; PeriodicOperationDivider opDivider5; PeriodicOperationDivider opDivider10; @@ -261,12 +354,14 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe void currentStateSetter(sd::SdCard sdCard, sd::SdState newState); void executeNextExternalSdCommand(); void checkExternalSdCommandStatus(); - void performRebootFileHandling(bool recreateFile); + void performRebootWatchdogHandling(bool recreateFile); + void performRebootCountersHandling(bool recreateFile); ReturnValue_t actionListDirectoryIntoFile(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t* data, size_t size); ReturnValue_t actionListDirectoryDumpDirectly(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t* data, size_t size); + ReturnValue_t performGracefulShutdown(xsc::Chip targetChip, xsc::Copy targetCopy); ReturnValue_t actionListDirectoryCommonCommandCreator(const uint8_t* data, size_t size, std::ostringstream& oss); @@ -277,16 +372,20 @@ class CoreController : public ExtendedControllerBase, public ReceivesParameterMe ReturnValue_t gracefulShutdownTasks(xsc::Chip chip, xsc::Copy copy, bool& protOpPerformed); ReturnValue_t handleProtInfoUpdateLine(std::string nextLine); - int handleBootCopyProtAtIndex(xsc::Chip targetChip, xsc::Copy targetCopy, bool protect, - bool& protOperationPerformed, bool selfChip, bool selfCopy, - bool allChips, bool allCopies, uint8_t arrIdx); - void determineAndExecuteReboot(RebootFile& rf, bool& needsReboot, xsc::Chip& tgtChip, - xsc::Copy& tgtCopy); - void resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy); + ReturnValue_t handleSwitchingSdCardsOffNonBlocking(); + bool handleBootCopyProt(xsc::Chip targetChip, xsc::Copy targetCopy, bool protect); + void rebootWatchdogAlgorithm(RebootWatchdogFile& rf, bool& needsReboot, xsc::Chip& tgtChip, + xsc::Copy& tgtCopy); + void resetRebootWatchdogCounters(xsc::Chip tgtChip, xsc::Copy tgtCopy); void setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::Copy tgtCopy); - bool parseRebootFile(std::string path, RebootFile& file); - void rewriteRebootFile(RebootFile file); + bool parseRebootWatchdogFile(std::string path, RebootWatchdogFile& file); + bool parseRebootCountersFile(std::string path, RebootCountersFile& file); + void rewriteRebootWatchdogFile(RebootWatchdogFile file); + void rewriteRebootCountersFile(RebootCountersFile file); void announceBootCounts(); + void announceVersionInfo(); + void announceCurrentImageInfo(); + void announceSdInfo(SdCardManager::SdStatePair sdStates); void readHkData(); void dirListingDumpHandler(); bool isNumber(const std::string& s); diff --git a/bsp_q7s/core/CoreDefinitions.h b/bsp_q7s/core/defs.h similarity index 81% rename from bsp_q7s/core/CoreDefinitions.h rename to bsp_q7s/core/defs.h index 91896301..a82c1c56 100644 --- a/bsp_q7s/core/CoreDefinitions.h +++ b/bsp_q7s/core/defs.h @@ -1,10 +1,16 @@ -#ifndef BSP_Q7S_CORE_COREDEFINITIONS_H_ -#define BSP_Q7S_CORE_COREDEFINITIONS_H_ +#ifndef BSP_Q7S_CORE_DEFS_H_ +#define BSP_Q7S_CORE_DEFS_H_ #include namespace core { +extern uint8_t FW_VERSION_MAJOR; +extern uint8_t FW_VERSION_MINOR; +extern uint8_t FW_VERSION_REVISION; +extern bool FW_VERSION_HAS_SHA; +extern char FW_VERSION_GIT_SHA[4]; + static const uint8_t HK_SET_ENTRIES = 3; static const uint32_t HK_SET_ID = 5; @@ -36,4 +42,4 @@ class HkSet : public StaticLocalDataSet { } // namespace core -#endif /* BSP_Q7S_CORE_COREDEFINITIONS_H_ */ +#endif /* BSP_Q7S_CORE_DEFS_H_ */ diff --git a/bsp_q7s/em/emObjectFactory.cpp b/bsp_q7s/em/emObjectFactory.cpp index 5270e887..3c3ba290 100644 --- a/bsp_q7s/em/emObjectFactory.cpp +++ b/bsp_q7s/em/emObjectFactory.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -10,8 +11,8 @@ #include "OBSWConfig.h" #include "bsp_q7s/core/CoreController.h" -#include "bsp_q7s/core/ObjectFactory.h" #include "busConf.h" +#include "common/config/devices/addresses.h" #include "devConf.h" #include "dummies/helperFactory.h" #include "eive/objects.h" @@ -35,18 +36,20 @@ void ObjectFactory::produce(void* args) { #endif PersistentTmStores stores; + readFirmwareVersion(); ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel, - *SdCardManager::instance(), &ipcStore, &tmStore, stores, - 200); + *SdCardManager::instance(), &ipcStore, &tmStore, stores, 200, + enableHkSets); LinuxLibgpioIF* gpioComIF = nullptr; SerialComIF* uartComIF = nullptr; SpiComIF* spiMainComIF = nullptr; I2cComIF* i2cComIF = nullptr; createCommunicationInterfaces(&gpioComIF, &uartComIF, &spiMainComIF, &i2cComIF); - /* Adding gpios for chip select decoding to the gpioComIf */ + // Adding GPIOs for chip select decoding and initializing them. q7s::gpioCallbacks::initSpiCsDecoder(gpioComIF); gpioCallbacks::disableAllDecoder(gpioComIF); + createPlI2cResetGpio(gpioComIF); // Hardware is usually not connected to EM, so we need to create dummies which replace lower // level components. @@ -59,17 +62,41 @@ void ObjectFactory::produce(void* args) { #if OBSW_ADD_PLOC_SUPERVISOR == 1 || OBSW_ADD_PLOC_MPSOC == 1 dummyCfg.addPlocDummies = false; #endif +#if OBSW_ADD_TMP_DEVICES == 1 + std::vector> tmpDevsToAdd = {{ + {objects::TMP1075_HANDLER_PLPCDU_0, addresses::TMP1075_PLPCDU_0}, + {objects::TMP1075_HANDLER_PLPCDU_1, addresses::TMP1075_PLPCDU_1}, + {objects::TMP1075_HANDLER_IF_BOARD, addresses::TMP1075_IF_BOARD}, + }}; + createTmpComponents(tmpDevsToAdd); + dummy::Tmp1075Cfg tmpCfg{}; + tmpCfg.addTcsBrd0 = true; + tmpCfg.addTcsBrd1 = true; + tmpCfg.addPlPcdu0 = false; + tmpCfg.addPlPcdu1 = false; + tmpCfg.addIfBrd = false; + dummyCfg.tmp1075Cfg = tmpCfg; +#endif #if OBSW_ADD_GOMSPACE_PCDU == 1 dummyCfg.addPowerDummies = false; // The ACU broke. dummyCfg.addOnlyAcuDummy = true; #endif +#if OBSW_ADD_STAR_TRACKER == 1 + dummyCfg.addStrDummy = false; +#endif +#if OBSW_ADD_SCEX_DEVICE == 0 + dummyCfg.addScexDummy = true; +#endif #if OBSW_ADD_BPX_BATTERY_HANDLER == 1 dummyCfg.addBpxBattDummy = false; #endif #if OBSW_ADD_ACS_BOARD == 1 dummyCfg.addAcsBoardDummies = false; #endif +#if OBSW_ADD_PL_PCDU == 0 + dummyCfg.addPlPcduDummy = true; +#endif PowerSwitchIF* pwrSwitcher = nullptr; #if OBSW_ADD_GOMSPACE_PCDU == 0 @@ -83,24 +110,12 @@ void ObjectFactory::produce(void* args) { new CoreController(objects::CORE_CONTROLLER, enableHkSets); - // Regular FM code, does not work for EM if the hardware is not connected - // createPcduComponents(gpioComIF, &pwrSwitcher); - // createPlPcduComponents(gpioComIF, spiMainComIF, pwrSwitcher); - // createSyrlinksComponents(pwrSwitcher); - // createSunSensorComponents(gpioComIF, spiMainComIF, pwrSwitcher, q7s::SPI_DEFAULT_DEV); - // createRtdComponents(q7s::SPI_DEFAULT_DEV, gpioComIF, pwrSwitcher, spiMainComIF); - // createTmpComponents(); - // createSolarArrayDeploymentComponents(); - // createPayloadComponents(gpioComIF); - // createHeaterComponents(gpioComIF, pwrSwitcher, healthTable); + auto* stackHandler = new Stack5VHandler(*pwrSwitcher); - // TODO: Careful! Switching this on somehow messes with the communication with the ProASIC - // and will cause xsc_boot_copy commands to always boot to 0 0 - // createRadSensorComponent(gpioComIF); + // Initialize chip select to avoid SPI bus issues. + createRadSensorChipSelect(gpioComIF); #if OBSW_ADD_ACS_BOARD == 1 - // Still initialize chip select to avoid SPI bus issues. - createRadSensorChipSelect(gpioComIF); createAcsBoardComponents(*spiMainComIF, gpioComIF, uartComIF, *pwrSwitcher, true, adis1650x::Type::ADIS16507); #else @@ -110,8 +125,14 @@ void ObjectFactory::produce(void* args) { gpioChecker(gpioComIF->addGpios(acsBoardGpios), "ACS Board"); #endif + const char* battAndImtqI2cDev = q7s::I2C_PL_EIVE; + if (core::FW_VERSION_MAJOR >= 4) { + battAndImtqI2cDev = q7s::I2C_PS_EIVE; + } + static_cast(battAndImtqI2cDev); + #if OBSW_ADD_MGT == 1 - createImtqComponents(pwrSwitcher, enableHkSets); + createImtqComponents(pwrSwitcher, enableHkSets, battAndImtqI2cDev); #endif #if OBSW_ADD_SYRLINKS == 1 @@ -123,13 +144,16 @@ void ObjectFactory::produce(void* args) { #endif #if OBSW_ADD_BPX_BATTERY_HANDLER == 1 - createBpxBatteryComponent(enableHkSets); + createBpxBatteryComponent(enableHkSets, battAndImtqI2cDev); #endif #if OBSW_ADD_STAR_TRACKER == 1 createStrComponents(pwrSwitcher); #endif /* OBSW_ADD_STAR_TRACKER == 1 */ +#if OBSW_ADD_PL_PCDU == 1 + createPlPcduComponents(gpioComIF, spiMainComIF, pwrSwitcher, *stackHandler); +#endif createPayloadComponents(gpioComIF, *pwrSwitcher); #if OBSW_ADD_CCSDS_IP_CORES == 1 @@ -155,6 +179,6 @@ void ObjectFactory::produce(void* args) { createAcsController(true, enableHkSets); HeaterHandler* heaterHandler; createHeaterComponents(gpioComIF, pwrSwitcher, healthTable, heaterHandler); - createThermalController(*heaterHandler); - satsystem::init(); + createThermalController(*heaterHandler, true); + satsystem::init(true); } diff --git a/bsp_q7s/fmObjectFactory.cpp b/bsp_q7s/fmObjectFactory.cpp index 5eeeef59..a3ab6bba 100644 --- a/bsp_q7s/fmObjectFactory.cpp +++ b/bsp_q7s/fmObjectFactory.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -7,9 +8,9 @@ #include "OBSWConfig.h" #include "bsp_q7s/core/CoreController.h" -#include "bsp_q7s/core/ObjectFactory.h" #include "busConf.h" #include "devConf.h" +#include "devices/addresses.h" #include "eive/objects.h" #include "fsfw_hal/linux/gpio/LinuxLibgpioIF.h" #include "linux/ObjectFactory.h" @@ -32,9 +33,10 @@ void ObjectFactory::produce(void* args) { #endif PersistentTmStores stores; + readFirmwareVersion(); ObjectFactory::produceGenericObjects(&healthTable, &pusFunnel, &cfdpFunnel, - *SdCardManager::instance(), &ipcStore, &tmStore, stores, - 200); + *SdCardManager::instance(), &ipcStore, &tmStore, stores, 200, + true); LinuxLibgpioIF* gpioComIF = nullptr; SerialComIF* uartComIF = nullptr; @@ -45,6 +47,7 @@ void ObjectFactory::produce(void* args) { /* Adding gpios for chip select decoding to the gpioComIf */ q7s::gpioCallbacks::initSpiCsDecoder(gpioComIF); gpioCallbacks::disableAllDecoder(gpioComIF); + createPlI2cResetGpio(gpioComIF); new CoreController(objects::CORE_CONTROLLER, enableHkSets); createPcduComponents(gpioComIF, &pwrSwitcher, enableHkSets); @@ -66,23 +69,38 @@ void ObjectFactory::produce(void* args) { HeaterHandler* heaterHandler; createHeaterComponents(gpioComIF, pwrSwitcher, healthTable, heaterHandler); #if OBSW_ADD_TMP_DEVICES == 1 - createTmpComponents(); + std::vector> tmpDevsToAdd = {{ + {objects::TMP1075_HANDLER_TCS_0, addresses::TMP1075_TCS_0}, + {objects::TMP1075_HANDLER_TCS_1, addresses::TMP1075_TCS_1}, + {objects::TMP1075_HANDLER_PLPCDU_0, addresses::TMP1075_PLPCDU_0}, + // damaged + // {objects::TMP1075_HANDLER_PLPCDU_1, addresses::TMP1075_PLPCDU_1}, + {objects::TMP1075_HANDLER_IF_BOARD, addresses::TMP1075_IF_BOARD}, + }}; + + createTmpComponents(tmpDevsToAdd); #endif createSolarArrayDeploymentComponents(*pwrSwitcher, *gpioComIF); +#if OBSW_ADD_PL_PCDU == 1 createPlPcduComponents(gpioComIF, spiMainComIF, pwrSwitcher, *stackHandler); +#endif #if OBSW_ADD_SYRLINKS == 1 createSyrlinksComponents(pwrSwitcher); #endif /* OBSW_ADD_SYRLINKS == 1 */ createRtdComponents(q7s::SPI_DEFAULT_DEV, gpioComIF, pwrSwitcher, spiMainComIF); createPayloadComponents(gpioComIF, *pwrSwitcher); + const char* battAndImtqI2cDev = q7s::I2C_PL_EIVE; + if (core::FW_VERSION_MAJOR >= 4) { + battAndImtqI2cDev = q7s::I2C_PS_EIVE; + } #if OBSW_ADD_MGT == 1 - createImtqComponents(pwrSwitcher, enableHkSets); + createImtqComponents(pwrSwitcher, enableHkSets, battAndImtqI2cDev); #endif createReactionWheelComponents(gpioComIF, pwrSwitcher); #if OBSW_ADD_BPX_BATTERY_HANDLER == 1 - createBpxBatteryComponent(enableHkSets); + createBpxBatteryComponent(enableHkSets, battAndImtqI2cDev); #endif #if OBSW_ADD_STAR_TRACKER == 1 @@ -112,7 +130,7 @@ void ObjectFactory::produce(void* args) { #endif /* OBSW_ADD_TEST_CODE == 1 */ createMiscComponents(); - createThermalController(*heaterHandler); + createThermalController(*heaterHandler, false); createAcsController(true, enableHkSets); - satsystem::init(); + satsystem::init(false); } diff --git a/bsp_q7s/fs/SdCardManager.cpp b/bsp_q7s/fs/SdCardManager.cpp index 14e3e6aa..ffbed668 100644 --- a/bsp_q7s/fs/SdCardManager.cpp +++ b/bsp_q7s/fs/SdCardManager.cpp @@ -125,13 +125,8 @@ ReturnValue_t SdCardManager::switchOnSdCard(sd::SdCard sdCard, bool doMountSdCar return mountSdCard(sdCard); } -ReturnValue_t SdCardManager::switchOffSdCard(sd::SdCard sdCard, bool doUnmountSdCard, - SdStatePair* statusPair) { - std::pair active; - ReturnValue_t result = getSdCardsStatus(active); - if (result != returnvalue::OK) { - return result; - } +ReturnValue_t SdCardManager::switchOffSdCard(sd::SdCard sdCard, SdStatePair& sdStates, + bool doUnmountSdCard) { if (doUnmountSdCard) { if (not blocking) { sif::warning << "SdCardManager::switchOffSdCard: Two-step command but manager is" @@ -147,17 +142,17 @@ ReturnValue_t SdCardManager::switchOffSdCard(sd::SdCard sdCard, bool doUnmountSd return returnvalue::FAILED; } if (sdCard == sd::SdCard::SLOT_0) { - if (active.first == sd::SdState::OFF) { + if (sdStates.first == sd::SdState::OFF) { return ALREADY_OFF; } } else if (sdCard == sd::SdCard::SLOT_1) { - if (active.second == sd::SdState::OFF) { + if (sdStates.second == sd::SdState::OFF) { return ALREADY_OFF; } } if (doUnmountSdCard) { - result = unmountSdCard(sdCard); + ReturnValue_t result = unmountSdCard(sdCard); if (result != returnvalue::OK) { return result; } @@ -189,7 +184,7 @@ ReturnValue_t SdCardManager::setSdCardState(sd::SdCard sdCard, bool on) { command << "q7hw sd set " << sdstring << " " << statestring; cmdExecutor.load(command.str(), blocking, printCmdOutput); ReturnValue_t result = cmdExecutor.execute(); - if (blocking and result != returnvalue::OK) { + if (result != returnvalue::OK) { utility::handleSystemError(cmdExecutor.getLastError(), "SdCardManager::setSdCardState"); } return result; @@ -204,6 +199,7 @@ ReturnValue_t SdCardManager::getSdCardsStatus(SdStatePair& sdStates) { ReturnValue_t SdCardManager::mountSdCard(sd::SdCard sdCard) { using namespace std; if (cmdExecutor.getCurrentState() == CommandExecutor::States::PENDING) { + sif::warning << "SdCardManager::mountSdCard: Command still pending" << std::endl; return CommandExecutor::COMMAND_PENDING; } if (sdCard == sd::SdCard::BOTH) { diff --git a/bsp_q7s/fs/SdCardManager.h b/bsp_q7s/fs/SdCardManager.h index 7a4a7cbe..23a3d193 100644 --- a/bsp_q7s/fs/SdCardManager.h +++ b/bsp_q7s/fs/SdCardManager.h @@ -114,8 +114,7 @@ class SdCardManager : public SystemObject, public SdCardMountedIF { * @return - returnvalue::OK on success, ALREADY_ON if it is already on, * SYSTEM_CALL_ERROR on system error */ - ReturnValue_t switchOffSdCard(sd::SdCard sdCard, bool doUnmountSdCard = true, - SdStatePair* statusPair = nullptr); + ReturnValue_t switchOffSdCard(sd::SdCard sdCard, SdStatePair& sdStates, bool doUnmountSdCard); /** * Get the state of the SD cards. If the state file does not exist, this function will diff --git a/bsp_q7s/core/ObjectFactory.cpp b/bsp_q7s/objectFactory.cpp similarity index 92% rename from bsp_q7s/core/ObjectFactory.cpp rename to bsp_q7s/objectFactory.cpp index cea90bf2..39271628 100644 --- a/bsp_q7s/core/ObjectFactory.cpp +++ b/bsp_q7s/objectFactory.cpp @@ -1,7 +1,9 @@ -#include "ObjectFactory.h" +#include "objectFactory.h" #include #include +#include +#include #include #include #include @@ -31,6 +33,9 @@ #include #include +#include +#include + #include "OBSWConfig.h" #include "bsp_q7s/boardtest/Q7STestTask.h" #include "bsp_q7s/callbacks/gnssCallback.h" @@ -99,6 +104,7 @@ #include +#include "bsp_q7s/core/defs.h" #include "fsfw/datapoollocal/LocalDataPoolManager.h" #include "fsfw/tmtcpacket/pus/tm.h" #include "fsfw/tmtcservices/CommandingServiceBase.h" @@ -129,6 +135,11 @@ ResetArgs RESET_ARGS_GNSS; std::atomic_bool LINK_STATE = CcsdsIpCoreHandler::LINK_DOWN; std::atomic_bool PTME_LOCKED = false; std::atomic_uint16_t I2C_FATAL_ERRORS = 0; +uint8_t core::FW_VERSION_MAJOR = 0; +uint8_t core::FW_VERSION_MINOR = 0; +uint8_t core::FW_VERSION_REVISION = 0; +bool core::FW_VERSION_HAS_SHA = false; +char core::FW_VERSION_GIT_SHA[4] = {}; void Factory::setStaticFrameworkObjectIds() { PusServiceBase::PUS_DISTRIBUTOR = objects::PUS_PACKET_DISTRIBUTOR; @@ -150,28 +161,23 @@ void Factory::setStaticFrameworkObjectIds() { void ObjectFactory::setStatics() { Factory::setStaticFrameworkObjectIds(); } -void ObjectFactory::createTmpComponents() { - std::vector> tmpDevIds = {{ - {objects::TMP1075_HANDLER_TCS_0, addresses::TMP1075_TCS_0}, - {objects::TMP1075_HANDLER_TCS_1, addresses::TMP1075_TCS_1}, - {objects::TMP1075_HANDLER_PLPCDU_0, addresses::TMP1075_PLPCDU_0}, - // damaged - // {objects::TMP1075_HANDLER_PLPCDU_1, addresses::TMP1075_PLPCDU_1}, - {objects::TMP1075_HANDLER_IF_BOARD, addresses::TMP1075_IF_BOARD}, - }}; +void ObjectFactory::createTmpComponents( + std::vector> tmpDevsToAdd) { + const char* tmpI2cDev = q7s::I2C_PS_EIVE; + if (core::FW_VERSION_MAJOR == 4) { + tmpI2cDev = q7s::I2C_PL_EIVE; + } else if (core::FW_VERSION_MAJOR >= 5) { + tmpI2cDev = q7s::I2C_Q7_EIVE; + } std::vector tmpDevCookies; - for (size_t idx = 0; idx < tmpDevIds.size(); idx++) { + for (size_t idx = 0; idx < tmpDevsToAdd.size(); idx++) { tmpDevCookies.push_back( - new I2cCookie(tmpDevIds[idx].second, TMP1075::MAX_REPLY_LENGTH, q7s::I2C_PS_EIVE)); + new I2cCookie(tmpDevsToAdd[idx].second, TMP1075::MAX_REPLY_LENGTH, tmpI2cDev)); auto* tmpDevHandler = - new Tmp1075Handler(tmpDevIds[idx].first, objects::I2C_COM_IF, tmpDevCookies[idx]); - tmpDevHandler->setCustomFdir(new TmpDevFdir(tmpDevIds[idx].first)); + new Tmp1075Handler(tmpDevsToAdd[idx].first, objects::I2C_COM_IF, tmpDevCookies[idx]); + tmpDevHandler->setCustomFdir(new TmpDevFdir(tmpDevsToAdd[idx].first)); tmpDevHandler->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); - // TODO: Remove this after TCS subsystem was added - // These devices are connected to the 3V3 stack and should be powered permanently. Therefore, - // we set them to normal mode immediately here. - tmpDevHandler->setModeNormal(); } } @@ -243,7 +249,7 @@ ReturnValue_t ObjectFactory::createRadSensorComponent(LinuxLibgpioIF* gpioComIF, createRadSensorChipSelect(gpioComIF); SpiCookie* spiCookieRadSensor = - new SpiCookie(addresses::RAD_SENSOR, gpioIds::CS_RAD_SENSOR, RAD_SENSOR::READ_SIZE, + new SpiCookie(addresses::RAD_SENSOR, gpioIds::CS_RAD_SENSOR, radSens::READ_SIZE, spi::DEFAULT_MAX_1227_MODE, spi::DEFAULT_MAX_1227_SPEED); spiCookieRadSensor->setMutexParams(MutexIF::TimeoutType::WAITING, spi::RAD_SENSOR_CS_TIMEOUT); auto radSensor = new RadiationSensorHandler(objects::RAD_SENSOR, objects::SPI_MAIN_COM_IF, @@ -505,7 +511,7 @@ void ObjectFactory::createAcsBoardComponents(SpiComIF& spiComIF, LinuxLibgpioIF* debugGps = true; #endif RESET_ARGS_GNSS.gpioComIF = gpioComIF; - RESET_ARGS_GNSS.waitPeriodMs = 100; + RESET_ARGS_GNSS.waitPeriodMs = 5; auto gpsCtrl = new GpsHyperionLinuxController(objects::GPS_CONTROLLER, objects::NO_OBJECT, enableHkSets, debugGps); gpsCtrl->setResetPinTriggerFunction(gps::triggerGpioResetPin, &RESET_ARGS_GNSS); @@ -730,20 +736,12 @@ ReturnValue_t ObjectFactory::createCcsdsComponents(CcsdsComponentArgs& args) { // GPIO definitions of signals connected to the virtual channel interfaces of the PTME IP Core GpioCookie* gpioCookiePtmeIp = new GpioCookie; GpiodRegularByLineName* gpio = nullptr; - gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC0, "PAPB VC0"); - gpioCookiePtmeIp->addGpio(gpioIds::VC0_PAPB_BUSY, gpio); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC0, "PAPB VC0"); gpioCookiePtmeIp->addGpio(gpioIds::VC0_PAPB_EMPTY, gpio); - gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC1, "PAPB VC1"); - gpioCookiePtmeIp->addGpio(gpioIds::VC1_PAPB_BUSY, gpio); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC1, "PAPB VC1"); gpioCookiePtmeIp->addGpio(gpioIds::VC1_PAPB_EMPTY, gpio); - gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC2, "PAPB VC2"); - gpioCookiePtmeIp->addGpio(gpioIds::VC2_PAPB_BUSY, gpio); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC2, "PAPB VC2"); gpioCookiePtmeIp->addGpio(gpioIds::VC2_PAPB_EMPTY, gpio); - gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_BUSY_SIGNAL_VC3, "PAPB VC3"); - gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_BUSY, gpio); gpio = new GpiodRegularByLineName(q7s::gpioNames::PAPB_EMPTY_SIGNAL_VC3, "PAPB VC3"); gpioCookiePtmeIp->addGpio(gpioIds::VC3_PAPB_EMPTY, gpio); gpio = new GpiodRegularByLineName(q7s::gpioNames::PTME_RESETN, "PTME RESETN", @@ -752,18 +750,14 @@ ReturnValue_t ObjectFactory::createCcsdsComponents(CcsdsComponentArgs& args) { gpioChecker(args.gpioComIF.addGpios(gpioCookiePtmeIp), "PTME PAPB VCs"); // Creating virtual channel interfaces - VirtualChannelIF* vc0 = - new PapbVcInterface(&args.gpioComIF, gpioIds::VC0_PAPB_BUSY, gpioIds::VC0_PAPB_EMPTY, - q7s::UIO_PTME, q7s::uiomapids::PTME_VC0); - VirtualChannelIF* vc1 = - new PapbVcInterface(&args.gpioComIF, gpioIds::VC1_PAPB_BUSY, gpioIds::VC1_PAPB_EMPTY, - q7s::UIO_PTME, q7s::uiomapids::PTME_VC1); - VirtualChannelIF* vc2 = - new PapbVcInterface(&args.gpioComIF, gpioIds::VC2_PAPB_BUSY, gpioIds::VC2_PAPB_EMPTY, - q7s::UIO_PTME, q7s::uiomapids::PTME_VC2); - VirtualChannelIF* vc3 = - new PapbVcInterface(&args.gpioComIF, gpioIds::VC3_PAPB_BUSY, gpioIds::VC3_PAPB_EMPTY, - q7s::UIO_PTME, q7s::uiomapids::PTME_VC3); + VirtualChannelIF* vc0 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC0_PAPB_EMPTY, + q7s::UIO_PTME, q7s::uiomapids::PTME_VC0); + VirtualChannelIF* vc1 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC1_PAPB_EMPTY, + q7s::UIO_PTME, q7s::uiomapids::PTME_VC1); + VirtualChannelIF* vc2 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC2_PAPB_EMPTY, + q7s::UIO_PTME, q7s::uiomapids::PTME_VC2); + VirtualChannelIF* vc3 = new PapbVcInterface(&args.gpioComIF, gpioIds::VC3_PAPB_EMPTY, + q7s::UIO_PTME, q7s::uiomapids::PTME_VC3); // Creating ptme object and adding virtual channel interfaces Ptme* ptme = new Ptme(objects::PTME); ptme->addVcInterface(ccsds::VC0, vc0); @@ -908,8 +902,6 @@ void ObjectFactory::createPlPcduComponents(LinuxLibgpioIF* gpioComIF, SpiComIF* new PayloadPcduHandler(objects::PLPCDU_HANDLER, objects::SPI_MAIN_COM_IF, spiCookie, gpioComIF, SdCardManager::instance(), stackHandler, false); spiCookie->setCallbackMode(PayloadPcduHandler::extConvAsTwoCallback, plPcduHandler); -// plPcduHandler->enablePeriodicPrintout(true, 5); -// static_cast(plPcduHandler); #if OBSW_TEST_PL_PCDU == 1 plPcduHandler->setStartUpImmediately(); #endif @@ -943,16 +935,7 @@ void ObjectFactory::createStrComponents(PowerSwitchIF* pwrSwitcher) { starTrackerCookie->setNoFixedSizeReply(); StrComHandler* strComIF = new StrComHandler(objects::STR_COM_IF); - const char* paramJsonFile = nullptr; -#ifdef EGSE - paramJsonFile = "/home/pi/arcsec/json/flight-config.json"; -#else -#if OBSW_STAR_TRACKER_GROUND_CONFIG == 1 - paramJsonFile = "/mnt/sd0/startracker/ground-config.json"; -#else - paramJsonFile = "/mnt/sd0/startracker/flight-config.json"; -#endif -#endif + const char* paramJsonFile = "/mnt/sd0/startracker/flight-config.json"; if (paramJsonFile == nullptr) { sif::error << "No valid Star Tracker parameter JSON file" << std::endl; } @@ -965,12 +948,13 @@ void ObjectFactory::createStrComponents(PowerSwitchIF* pwrSwitcher) { starTracker->setCustomFdir(strFdir); } -void ObjectFactory::createImtqComponents(PowerSwitchIF* pwrSwitcher, bool enableHkSets) { +void ObjectFactory::createImtqComponents(PowerSwitchIF* pwrSwitcher, bool enableHkSets, + const char* i2cDev) { auto* imtqAssy = new ImtqAssembly(objects::IMTQ_ASSY); imtqAssy->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM); new ImtqPollingTask(objects::IMTQ_POLLING, I2C_FATAL_ERRORS); - I2cCookie* imtqI2cCookie = new I2cCookie(addresses::IMTQ, imtq::MAX_REPLY_SIZE, q7s::I2C_PL_EIVE); + I2cCookie* imtqI2cCookie = new I2cCookie(addresses::IMTQ, imtq::MAX_REPLY_SIZE, i2cDev); auto imtqHandler = new ImtqHandler(objects::IMTQ_HANDLER, objects::IMTQ_POLLING, imtqI2cCookie, power::Switches::PDU1_CH3_MGT_5V, enableHkSets); imtqHandler->enableThermalModule(ThermalStateCfg()); @@ -986,8 +970,8 @@ void ObjectFactory::createImtqComponents(PowerSwitchIF* pwrSwitcher, bool enable #endif } -void ObjectFactory::createBpxBatteryComponent(bool enableHkSets) { - I2cCookie* bpxI2cCookie = new I2cCookie(addresses::BPX_BATTERY, 100, q7s::I2C_PL_EIVE); +void ObjectFactory::createBpxBatteryComponent(bool enableHkSets, const char* i2cDev) { + I2cCookie* bpxI2cCookie = new I2cCookie(addresses::BPX_BATTERY, 100, i2cDev); BpxBatteryHandler* bpxHandler = new BpxBatteryHandler( objects::BPX_BATT_HANDLER, objects::I2C_COM_IF, bpxI2cCookie, enableHkSets); bpxHandler->setStartUpImmediately(); @@ -1025,3 +1009,50 @@ void ObjectFactory::createRadSensorChipSelect(LinuxLibgpioIF* gpioIF) { gpioCookieRadSensor->addGpio(gpioIds::ENABLE_RADFET, gpio); gpioChecker(gpioIF->addGpios(gpioCookieRadSensor), "RAD sensor"); } + +void ObjectFactory::createPlI2cResetGpio(LinuxLibgpioIF* gpioIF) { + using namespace gpio; + if (common::OBSW_VERSION_MAJOR >= 6 or common::OBSW_VERSION_MAJOR == 4) { + if (gpioIF == nullptr) { + return; + } + GpioCookie* gpioI2cResetnCookie = new GpioCookie; + GpiodRegularByLineName* gpioI2cResetn = new GpiodRegularByLineName( + q7s::gpioNames::PL_I2C_ARESETN, "PL_I2C_ARESETN", Direction::OUT, Levels::HIGH); + gpioI2cResetnCookie->addGpio(gpioIds::PL_I2C_ARESETN, gpioI2cResetn); + gpioChecker(gpioIF->addGpios(gpioI2cResetnCookie), "PL I2C ARESETN"); + // Reset I2C explicitely again. + gpioIF->pullLow(gpioIds::PL_I2C_ARESETN); + TaskFactory::delayTask(1); + gpioIF->pullHigh(gpioIds::PL_I2C_ARESETN); + } +} + +ReturnValue_t ObjectFactory::readFirmwareVersion() { + uint32_t* mappedSysRomAddr = nullptr; + // The SYS ROM FPGA block is only available in those versions. + if (not(common::OBSW_VERSION_MAJOR >= 6) or (common::OBSW_VERSION_MAJOR == 4)) { + return returnvalue::OK; + } + // This has to come before the version announce because it might be required for retrieving + // the firmware version. + UioMapper sysRomMapper(q7s::UIO_SYS_ROM); + ReturnValue_t result = + sysRomMapper.getMappedAdress(&mappedSysRomAddr, UioMapper::Permissions::READ_ONLY); + if (result != returnvalue::OK) { + sif::error << "Getting mapped SYS ROM UIO address failed" << std::endl; + return returnvalue::FAILED; + } + if (mappedSysRomAddr != nullptr) { + uint32_t firstEntry = *(reinterpret_cast(mappedSysRomAddr)); + uint32_t secondEntry = *(reinterpret_cast(mappedSysRomAddr) + 1); + core::FW_VERSION_MAJOR = (firstEntry >> 24) & 0xff; + core::FW_VERSION_MINOR = (firstEntry >> 16) & 0xff; + core::FW_VERSION_REVISION = (firstEntry >> 8) & 0xff; + bool hasGitSha = (firstEntry & 0x0b1); + if (hasGitSha) { + std::memcpy(core::FW_VERSION_GIT_SHA, &secondEntry, 4); + } + } + return returnvalue::OK; +} diff --git a/bsp_q7s/core/ObjectFactory.h b/bsp_q7s/objectFactory.h similarity index 92% rename from bsp_q7s/core/ObjectFactory.h rename to bsp_q7s/objectFactory.h index df65e1ae..491720ac 100644 --- a/bsp_q7s/core/ObjectFactory.h +++ b/bsp_q7s/objectFactory.h @@ -58,7 +58,7 @@ void createPcduComponents(LinuxLibgpioIF* gpioComIF, PowerSwitchIF** pwrSwitcher bool enableHkSets); void createPlPcduComponents(LinuxLibgpioIF* gpioComIF, SpiComIF* spiComIF, PowerSwitchIF* pwrSwitcher, Stack5VHandler& stackHandler); -void createTmpComponents(); +void createTmpComponents(std::vector> tmpDevsToAdd); void createRadSensorChipSelect(LinuxLibgpioIF* gpioIF); ReturnValue_t createRadSensorComponent(LinuxLibgpioIF* gpioComIF, Stack5VHandler& handler); void createAcsBoardGpios(GpioCookie& cookie); @@ -67,17 +67,19 @@ void createAcsBoardComponents(SpiComIF& spiComIF, LinuxLibgpioIF* gpioComIF, Ser adis1650x::Type adisType); void createHeaterComponents(GpioIF* gpioIF, PowerSwitchIF* pwrSwitcher, HealthTableIF* healthTable, HeaterHandler*& heaterHandler); -void createImtqComponents(PowerSwitchIF* pwrSwitcher, bool enableHkSets); -void createBpxBatteryComponent(bool enableHkSets); +void createImtqComponents(PowerSwitchIF* pwrSwitcher, bool enableHkSets, const char* i2cDev); +void createBpxBatteryComponent(bool enableHkSets, const char* i2cDev); void createStrComponents(PowerSwitchIF* pwrSwitcher); void createSolarArrayDeploymentComponents(PowerSwitchIF& pwrSwitcher, GpioIF& gpioIF); void createSyrlinksComponents(PowerSwitchIF* pwrSwitcher); void createPayloadComponents(LinuxLibgpioIF* gpioComIF, PowerSwitchIF& pwrSwitcher); void createReactionWheelComponents(LinuxLibgpioIF* gpioComIF, PowerSwitchIF* pwrSwitcher); ReturnValue_t createCcsdsComponents(CcsdsComponentArgs& args); +ReturnValue_t readFirmwareVersion(); void createMiscComponents(); void createTestComponents(LinuxLibgpioIF* gpioComIF); +void createPlI2cResetGpio(LinuxLibgpioIF* gpioComIF); void testAcsBrdAss(AcsBoardAssembly* assAss); diff --git a/bsp_q7s/obsw.cpp b/bsp_q7s/obsw.cpp index dc547b03..fc49c369 100644 --- a/bsp_q7s/obsw.cpp +++ b/bsp_q7s/obsw.cpp @@ -11,13 +11,13 @@ #include "OBSWConfig.h" #include "bsp_q7s/core/WatchdogHandler.h" #include "commonConfig.h" -#include "core/scheduling.h" #include "fsfw/tasks/TaskFactory.h" #include "fsfw/version.h" #include "mission/acs/defs.h" #include "mission/com/defs.h" #include "mission/system/systemTree.h" #include "q7sConfig.h" +#include "scheduling.h" #include "watchdog/definitions.h" static constexpr int OBSW_ALREADY_RUNNING = -2; diff --git a/bsp_q7s/core/scheduling.cpp b/bsp_q7s/scheduling.cpp similarity index 98% rename from bsp_q7s/core/scheduling.cpp rename to bsp_q7s/scheduling.cpp index 71da5bdc..d7c179ad 100644 --- a/bsp_q7s/core/scheduling.cpp +++ b/bsp_q7s/scheduling.cpp @@ -9,7 +9,6 @@ #include #include "OBSWConfig.h" -#include "bsp_q7s/core/ObjectFactory.h" #include "fsfw/objectmanager/ObjectManager.h" #include "fsfw/objectmanager/ObjectManagerIF.h" #include "fsfw/platform.h" @@ -21,6 +20,8 @@ #include "mission/pollingSeqTables.h" #include "mission/scheduling.h" #include "mission/utility/InitMission.h" +#include "objectFactory.h" +#include "q7sConfig.h" /* This is configured for linux without CR */ #ifdef PLATFORM_UNIX @@ -324,6 +325,10 @@ void scheduling::initTasks() { if (result != returnvalue::OK) { scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER); } + result = tcsSystemTask->addComponent(objects::HEATER_HANDLER); + if (result != returnvalue::OK) { + scheduling::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER); + } #if OBSW_ADD_SYRLINKS == 1 PeriodicTaskIF* syrlinksCom = factory->createPeriodicTask( @@ -527,7 +532,15 @@ void scheduling::createPstTasks(TaskFactory& factory, TaskDeadlineMissedFunction FixedTimeslotTaskIF* i2cPst = factory.createFixedTimeslotTask("I2C_PS_PST", 60, PeriodicTaskIF::MINIMUM_STACK_SIZE * 4, 0.6, missedDeadlineFunc, &RR_SCHEDULING); - result = pst::pstI2cProcessingSystem(i2cPst); + pst::TmpSchedConfig tmpSchedConf; +#if OBSW_Q7S_EM == 1 + tmpSchedConf.scheduleTmpDev0 = true; + tmpSchedConf.scheduleTmpDev1 = true; + tmpSchedConf.schedulePlPcduDev0 = true; + tmpSchedConf.schedulePlPcduDev1 = true; + tmpSchedConf.scheduleIfBoardDev = true; +#endif + result = pst::pstI2c(tmpSchedConf, i2cPst); if (result != returnvalue::OK) { if (result == FixedTimeslotTaskIF::SLOT_LIST_EMPTY) { sif::warning << "scheduling::initTasks: I2C PST is empty" << std::endl; diff --git a/bsp_q7s/core/scheduling.h b/bsp_q7s/scheduling.h similarity index 100% rename from bsp_q7s/core/scheduling.h rename to bsp_q7s/scheduling.h diff --git a/bsp_q7s/xadc/Xadc.cpp b/bsp_q7s/xadc/Xadc.cpp index 34a4e159..da3fbf7f 100644 --- a/bsp_q7s/xadc/Xadc.cpp +++ b/bsp_q7s/xadc/Xadc.cpp @@ -129,7 +129,7 @@ ReturnValue_t Xadc::readValFromFile(const char* filename, T& val) { sif::warning << "Xadc::readValFromFile: Failed to open file " << filename << std::endl; return returnvalue::FAILED; } - char valstring[MAX_STR_LENGTH] = ""; + char valstring[MAX_STR_LENGTH]{}; char* returnVal = fgets(valstring, MAX_STR_LENGTH, fp); if (returnVal == nullptr) { sif::warning << "Xadc::readValFromFile: Failed to read string from file " << filename @@ -139,6 +139,11 @@ ReturnValue_t Xadc::readValFromFile(const char* filename, T& val) { } std::istringstream valSstream(valstring); valSstream >> val; + if (valSstream.bad()) { + sif::warning << "Xadc: Conversion of value to target type failed" << std::endl; + fclose(fp); + return returnvalue::FAILED; + } fclose(fp); return returnvalue::OK; } diff --git a/common/config/devices/gpioIds.h b/common/config/devices/gpioIds.h index 640f4ead..bed82142 100644 --- a/common/config/devices/gpioIds.h +++ b/common/config/devices/gpioIds.h @@ -77,6 +77,8 @@ enum gpioId_t { CS_RAD_SENSOR, ENABLE_RADFET, + PL_I2C_ARESETN, + PAPB_BUSY_N, PAPB_EMPTY, @@ -93,15 +95,10 @@ enum gpioId_t { EN_RW_CS, SPI_MUX, - VC0_PAPB_EMPTY, - VC0_PAPB_BUSY, VC1_PAPB_EMPTY, - VC1_PAPB_BUSY, VC2_PAPB_EMPTY, - VC2_PAPB_BUSY, VC3_PAPB_EMPTY, - VC3_PAPB_BUSY, PTME_RESETN, PDEC_RESET, diff --git a/common/config/eive/definitions.h b/common/config/eive/definitions.h index 2083962a..b369f512 100644 --- a/common/config/eive/definitions.h +++ b/common/config/eive/definitions.h @@ -11,6 +11,8 @@ static constexpr char SD_1_MOUNT_POINT[] = "/mnt/sd1"; static constexpr char OBSW_UPDATE_ARCHIVE_FILE_NAME[] = "eive-sw-update.tar.xz"; static constexpr char STRIPPED_OBSW_BINARY_FILE_NAME[] = "eive-obsw-stripped"; static constexpr char OBSW_VERSION_FILE_NAME[] = "obsw_version.txt"; +static constexpr char PUS_SEQUENCE_COUNT_FILE[] = "pus-sequence-count.txt"; +static constexpr char CFDP_SEQUENCE_COUNT_FILE[] = "cfdp-sequence-count.txt"; static constexpr char OBSW_PATH[] = "/usr/bin/eive-obsw"; static constexpr char OBSW_VERSION_FILE_PATH[] = "/usr/share/eive-obsw/obsw_version.txt"; diff --git a/dummies/CMakeLists.txt b/dummies/CMakeLists.txt index 6a49fcc6..bc8ab837 100644 --- a/dummies/CMakeLists.txt +++ b/dummies/CMakeLists.txt @@ -19,6 +19,7 @@ target_sources( GpsCtrlDummy.cpp GyroAdisDummy.cpp GyroL3GD20Dummy.cpp + RadSensorDummy.cpp MgmLIS3MDLDummy.cpp PlPcduDummy.cpp ExecutableComIfDummy.cpp diff --git a/dummies/CoreControllerDummy.cpp b/dummies/CoreControllerDummy.cpp index 8a027dbf..df2bef03 100644 --- a/dummies/CoreControllerDummy.cpp +++ b/dummies/CoreControllerDummy.cpp @@ -1,6 +1,6 @@ #include "CoreControllerDummy.h" -#include +#include #include #include diff --git a/dummies/ImtqDummy.cpp b/dummies/ImtqDummy.cpp index 0c8f9076..8570a9be 100644 --- a/dummies/ImtqDummy.cpp +++ b/dummies/ImtqDummy.cpp @@ -5,14 +5,19 @@ ImtqDummy::ImtqDummy(object_id_t objectId, object_id_t comif, CookieIF *comCookie, power::Switch_t pwrSwitcher, bool enableHkSets) : DeviceHandlerBase(objectId, comif, comCookie), - setNoTorque(this), - setWithTorque(this), enableHkSets(enableHkSets), + statusSet(this), + dipoleSet(*this), + rawMtmNoTorque(this), + hkDatasetNoTorque(this), + rawMtmWithTorque(this), + hkDatasetWithTorque(this), + calMtmMeasurementSet(this), switcher(pwrSwitcher) {} ImtqDummy::~ImtqDummy() = default; -void ImtqDummy::doStartUp() { setMode(MODE_NORMAL); } +void ImtqDummy::doStartUp() { setMode(MODE_ON); } void ImtqDummy::doShutDown() { setMode(_MODE_POWER_DOWN); } @@ -79,17 +84,37 @@ ReturnValue_t ImtqDummy::initializeLocalDataPool(localpool::DataPool &localDataP localDataPoolMap.emplace(imtq::MCU_TEMPERATURE_WT, new PoolEntry({0})); poolManager.subscribeForDiagPeriodicPacket( - subdp::DiagnosticsHkPeriodicParams(setNoTorque.getSid(), enableHkSets, 30.0)); + subdp::DiagnosticsHkPeriodicParams(hkDatasetNoTorque.getSid(), enableHkSets, 30.0)); poolManager.subscribeForDiagPeriodicPacket( - subdp::DiagnosticsHkPeriodicParams(setWithTorque.getSid(), enableHkSets, 30.0)); + subdp::DiagnosticsHkPeriodicParams(hkDatasetWithTorque.getSid(), enableHkSets, 30.0)); + poolManager.subscribeForDiagPeriodicPacket( + subdp::DiagnosticsHkPeriodicParams(rawMtmNoTorque.getSid(), false, 10.0)); + poolManager.subscribeForDiagPeriodicPacket( + subdp::DiagnosticsHkPeriodicParams(rawMtmWithTorque.getSid(), false, 10.0)); + poolManager.subscribeForDiagPeriodicPacket( + subdp::DiagnosticsHkPeriodicParams(calMtmMeasurementSet.getSid(), false, 10.0)); + poolManager.subscribeForRegularPeriodicPacket( + subdp::RegularHkPeriodicParams(statusSet.getSid(), false, 10.0)); + poolManager.subscribeForDiagPeriodicPacket( + subdp::DiagnosticsHkPeriodicParams(dipoleSet.getSid(), false, 10.0)); return DeviceHandlerBase::initializeLocalDataPool(localDataPoolMap, poolManager); } LocalPoolDataSetBase *ImtqDummy::getDataSetHandle(sid_t sid) { - if (sid == setNoTorque.getSid()) { - return &setNoTorque; - } else if (sid == setWithTorque.getSid()) { - return &setWithTorque; + if (sid == hkDatasetNoTorque.getSid()) { + return &hkDatasetNoTorque; + } else if (sid == dipoleSet.getSid()) { + return &dipoleSet; + } else if (sid == statusSet.getSid()) { + return &statusSet; + } else if (sid == hkDatasetWithTorque.getSid()) { + return &hkDatasetWithTorque; + } else if (sid == rawMtmWithTorque.getSid()) { + return &rawMtmWithTorque; + } else if (sid == calMtmMeasurementSet.getSid()) { + return &calMtmMeasurementSet; + } else if (sid == rawMtmNoTorque.getSid()) { + return &rawMtmNoTorque; } return nullptr; } diff --git a/dummies/ImtqDummy.h b/dummies/ImtqDummy.h index 990df6e0..8c76affc 100644 --- a/dummies/ImtqDummy.h +++ b/dummies/ImtqDummy.h @@ -18,11 +18,18 @@ class ImtqDummy : public DeviceHandlerBase { ~ImtqDummy() override; protected: - ReturnValue_t getSwitches(const uint8_t **switches, uint8_t *numberOfSwitches) override; - imtq::HkDatasetNoTorque setNoTorque; - imtq::HkDatasetWithTorque setWithTorque; bool enableHkSets; + imtq::StatusDataset statusSet; + imtq::DipoleActuationSet dipoleSet; + imtq::RawMtmMeasurementNoTorque rawMtmNoTorque; + imtq::HkDatasetNoTorque hkDatasetNoTorque; + + imtq::RawMtmMeasurementWithTorque rawMtmWithTorque; + imtq::HkDatasetWithTorque hkDatasetWithTorque; + + imtq::CalibratedMtmMeasurementSet calMtmMeasurementSet; + PoolEntry statusMode = PoolEntry({0}); PoolEntry statusError = PoolEntry({0}); PoolEntry statusConfig = PoolEntry({0}); @@ -42,6 +49,8 @@ class ImtqDummy : public DeviceHandlerBase { power::Switch_t switcher = power::NO_SWITCH; + ReturnValue_t getSwitches(const uint8_t **switches, uint8_t *numberOfSwitches) override; + void doStartUp() override; void doShutDown() override; ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override; diff --git a/dummies/Max31865Dummy.cpp b/dummies/Max31865Dummy.cpp index 6ec1a716..99fc336a 100644 --- a/dummies/Max31865Dummy.cpp +++ b/dummies/Max31865Dummy.cpp @@ -7,15 +7,21 @@ using namespace returnvalue; Max31865Dummy::Max31865Dummy(object_id_t objectId, object_id_t comif, CookieIF *comCookie) : DeviceHandlerBase(objectId, comif, comCookie), set(this, EiveMax31855::EXCHANGE_SET_ID) {} void Max31865Dummy::doStartUp() { setMode(MODE_ON); } -void Max31865Dummy::doShutDown() { setMode(_MODE_POWER_DOWN); } +void Max31865Dummy::doShutDown() { + PoolReadGuard pg(&set); + set.setValidity(false, true); + setMode(MODE_OFF); +} ReturnValue_t Max31865Dummy::buildNormalDeviceCommand(DeviceCommandId_t *id) { return NOTHING_TO_SEND; } -ReturnValue_t Max31865Dummy::buildTransitionDeviceCommand(DeviceCommandId_t *id) { return OK; } +ReturnValue_t Max31865Dummy::buildTransitionDeviceCommand(DeviceCommandId_t *id) { + return NOTHING_TO_SEND; +} ReturnValue_t Max31865Dummy::buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData, size_t commandDataLen) { - return 0; + return NOTHING_TO_SEND; } ReturnValue_t Max31865Dummy::scanForReply(const uint8_t *start, size_t len, DeviceCommandId_t *foundId, size_t *foundLen) { diff --git a/dummies/PlPcduDummy.cpp b/dummies/PlPcduDummy.cpp index 6bfc271f..7dfa4464 100644 --- a/dummies/PlPcduDummy.cpp +++ b/dummies/PlPcduDummy.cpp @@ -7,7 +7,7 @@ PlPcduDummy::PlPcduDummy(object_id_t objectId, object_id_t comif, CookieIF *comC PlPcduDummy::~PlPcduDummy() {} -void PlPcduDummy::doStartUp() { setMode(MODE_NORMAL); } +void PlPcduDummy::doStartUp() { setMode(MODE_ON); } void PlPcduDummy::doShutDown() { setMode(MODE_OFF); } diff --git a/dummies/RadSensorDummy.cpp b/dummies/RadSensorDummy.cpp new file mode 100644 index 00000000..5c42c594 --- /dev/null +++ b/dummies/RadSensorDummy.cpp @@ -0,0 +1,55 @@ +#include "RadSensorDummy.h" + +RadSensorDummy::RadSensorDummy(object_id_t objectId, object_id_t comif, CookieIF *comCookie) + : DeviceHandlerBase(objectId, comif, comCookie), sensorSet(this) {} + +RadSensorDummy::~RadSensorDummy() {} + +void RadSensorDummy::doStartUp() { setMode(MODE_ON); } + +void RadSensorDummy::doShutDown() { setMode(MODE_OFF); } + +ReturnValue_t RadSensorDummy::buildNormalDeviceCommand(DeviceCommandId_t *id) { + return NOTHING_TO_SEND; +} + +ReturnValue_t RadSensorDummy::buildTransitionDeviceCommand(DeviceCommandId_t *id) { + return NOTHING_TO_SEND; +} + +ReturnValue_t RadSensorDummy::buildCommandFromCommand(DeviceCommandId_t deviceCommand, + const uint8_t *commandData, + size_t commandDataLen) { + return returnvalue::OK; +} + +ReturnValue_t RadSensorDummy::scanForReply(const uint8_t *start, size_t len, + DeviceCommandId_t *foundId, size_t *foundLen) { + return returnvalue::OK; +} + +ReturnValue_t RadSensorDummy::interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) { + return returnvalue::OK; +} + +void RadSensorDummy::fillCommandAndReplyMap() {} + +uint32_t RadSensorDummy::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return 500; } + +ReturnValue_t RadSensorDummy::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, + LocalDataPoolManager &poolManager) { + localDataPoolMap.emplace(radSens::TEMPERATURE_C, new PoolEntry({0.0})); + localDataPoolMap.emplace(radSens::AIN0, new PoolEntry({0})); + localDataPoolMap.emplace(radSens::AIN1, new PoolEntry({0})); + localDataPoolMap.emplace(radSens::AIN4, new PoolEntry({0})); + localDataPoolMap.emplace(radSens::AIN5, new PoolEntry({0})); + localDataPoolMap.emplace(radSens::AIN6, new PoolEntry({0})); + localDataPoolMap.emplace(radSens::AIN7, new PoolEntry({0})); + poolManager.subscribeForRegularPeriodicPacket( + subdp::RegularHkPeriodicParams(sensorSet.getSid(), false, 20.0)); + return returnvalue::OK; + + return returnvalue::OK; +} + +LocalPoolDataSetBase *RadSensorDummy::getDataSetHandle(sid_t sid) { return &sensorSet; } diff --git a/dummies/RadSensorDummy.h b/dummies/RadSensorDummy.h new file mode 100644 index 00000000..a3edf4f8 --- /dev/null +++ b/dummies/RadSensorDummy.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "mission/payload/radSensorDefinitions.h" + +class RadSensorDummy : public DeviceHandlerBase { + public: + static const DeviceCommandId_t SIMPLE_COMMAND = 1; + static const DeviceCommandId_t PERIODIC_REPLY = 2; + + static const uint8_t SIMPLE_COMMAND_DATA = 1; + static const uint8_t PERIODIC_REPLY_DATA = 2; + + RadSensorDummy(object_id_t objectId, object_id_t comif, CookieIF *comCookie); + virtual ~RadSensorDummy(); + + protected: + radSens::RadSensorDataset sensorSet; + + void doStartUp() override; + void doShutDown() override; + ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override; + ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t *id) override; + ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData, + size_t commandDataLen) override; + ReturnValue_t scanForReply(const uint8_t *start, size_t len, DeviceCommandId_t *foundId, + size_t *foundLen) override; + ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) override; + void fillCommandAndReplyMap() override; + uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override; + ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap, + LocalDataPoolManager &poolManager) override; + LocalPoolDataSetBase *getDataSetHandle(sid_t sid) override; +}; diff --git a/dummies/RwDummy.cpp b/dummies/RwDummy.cpp index 60652ef7..0bf4db42 100644 --- a/dummies/RwDummy.cpp +++ b/dummies/RwDummy.cpp @@ -3,13 +3,24 @@ #include RwDummy::RwDummy(object_id_t objectId, object_id_t comif, CookieIF *comCookie) - : DeviceHandlerBase(objectId, comif, comCookie) {} + : DeviceHandlerBase(objectId, comif, comCookie), + + statusSet(this), + lastResetStatusSet(this), + tmDataset(this), + rwSpeedActuationSet(*this) {} RwDummy::~RwDummy() {} -void RwDummy::doStartUp() { setMode(MODE_ON); } +void RwDummy::doStartUp() { + statusSet.setReportingEnabled(true); + setMode(MODE_ON); +} -void RwDummy::doShutDown() { setMode(MODE_OFF); } +void RwDummy::doShutDown() { + statusSet.setReportingEnabled(false); + setMode(MODE_OFF); +} ReturnValue_t RwDummy::buildNormalDeviceCommand(DeviceCommandId_t *id) { return NOTHING_TO_SEND; } @@ -74,5 +85,11 @@ ReturnValue_t RwDummy::initializeLocalDataPool(localpool::DataPool &localDataPoo localDataPoolMap.emplace(rws::SPI_BYTES_READ, new PoolEntry({0})); localDataPoolMap.emplace(rws::SPI_REG_OVERRUN_ERRORS, new PoolEntry({0})); localDataPoolMap.emplace(rws::SPI_TOTAL_ERRORS, new PoolEntry({0})); + poolManager.subscribeForDiagPeriodicPacket( + subdp::DiagnosticsHkPeriodicParams(statusSet.getSid(), false, 12.0)); + poolManager.subscribeForRegularPeriodicPacket( + subdp::RegularHkPeriodicParams(tmDataset.getSid(), false, 30.0)); + poolManager.subscribeForRegularPeriodicPacket( + subdp::RegularHkPeriodicParams(lastResetStatusSet.getSid(), false, 30.0)); return returnvalue::OK; } diff --git a/dummies/RwDummy.h b/dummies/RwDummy.h index 5673717a..03629937 100644 --- a/dummies/RwDummy.h +++ b/dummies/RwDummy.h @@ -2,6 +2,7 @@ #define DUMMIES_RWDUMMY_H_ #include +#include class RwDummy : public DeviceHandlerBase { public: @@ -15,6 +16,11 @@ class RwDummy : public DeviceHandlerBase { virtual ~RwDummy(); protected: + rws::StatusSet statusSet; + rws::LastResetSatus lastResetStatusSet; + rws::TmDataset tmDataset; + rws::RwSpeedActuationSet rwSpeedActuationSet; + PoolEntry rwSpeed = PoolEntry({0}); PoolEntry rampTime = PoolEntry({10}); diff --git a/dummies/SusDummy.cpp b/dummies/SusDummy.cpp index 75e36676..3e03f198 100644 --- a/dummies/SusDummy.cpp +++ b/dummies/SusDummy.cpp @@ -5,7 +5,7 @@ SusDummy::SusDummy(object_id_t objectId, object_id_t comif, CookieIF *comCookie) SusDummy::~SusDummy() {} -void SusDummy::doStartUp() { setMode(MODE_NORMAL); } +void SusDummy::doStartUp() { setMode(MODE_ON); } void SusDummy::doShutDown() { setMode(MODE_OFF); } diff --git a/dummies/TemperatureSensorInserter.cpp b/dummies/TemperatureSensorInserter.cpp index 14a005aa..942231f5 100644 --- a/dummies/TemperatureSensorInserter.cpp +++ b/dummies/TemperatureSensorInserter.cpp @@ -7,9 +7,9 @@ #include #include -TemperatureSensorInserter::TemperatureSensorInserter(object_id_t objectId, - Max31865DummyMap tempSensorDummies_, - Tmp1075DummyMap tempTmpSensorDummies_) +TemperatureSensorInserter::TemperatureSensorInserter( + object_id_t objectId, Max31865DummyMap tempSensorDummies_, + std::optional tempTmpSensorDummies_) : SystemObject(objectId), max31865DummyMap(std::move(tempSensorDummies_)), tmp1075DummyMap(std::move(tempTmpSensorDummies_)) {} @@ -25,8 +25,10 @@ ReturnValue_t TemperatureSensorInserter::performOperation(uint8_t opCode) { for (auto& rtdDummy : max31865DummyMap) { rtdDummy.second->setTemperature(10, true); } - for (auto& tmpDummy : tmp1075DummyMap) { - tmpDummy.second->setTemperature(10, true); + if (tmp1075DummyMap.has_value()) { + for (auto& tmpDummy : tmp1075DummyMap.value()) { + tmpDummy.second->setTemperature(10, true); + } } tempsWereInitialized = true; } @@ -96,6 +98,25 @@ ReturnValue_t TemperatureSensorInserter::performOperation(uint8_t opCode) { } break; } + case (TestCase::COLD_PLOC_CONSECUTIVE): { + if (cycles == 15) { + sif::debug << "Setting cold PLOC temperature" << std::endl; + max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(-15, true); + } + if (cycles == 30) { + sif::debug << "Setting warmer PLOC temperature" << std::endl; + max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(0, true); + } + if (cycles == 45) { + sif::debug << "Setting cold PLOC temperature again" << std::endl; + max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(-15, true); + } + if (cycles == 60) { + sif::debug << "Setting warmer PLOC temperature again" << std::endl; + max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(0, true); + } + break; + } case (TestCase::COLD_CAMERA): { if (cycles == 15) { sif::debug << "Setting cold CAM temperature" << std::endl; @@ -105,6 +126,21 @@ ReturnValue_t TemperatureSensorInserter::performOperation(uint8_t opCode) { sif::debug << "Setting CAM temperature back to normal" << std::endl; max31865DummyMap[objects::RTD_2_IC5_4K_CAMERA]->setTemperature(0, true); } + break; + } + case (TestCase::COLD_PLOC_STAYS_COLD): { + if (cycles == 15) { + sif::debug << "Setting cold PLOC temperature" << std::endl; + max31865DummyMap[objects::RTD_0_IC3_PLOC_HEATSPREADER]->setTemperature(-40, true); + } + break; + } + case (TestCase::COLD_CAMERA_STAYS_COLD): { + if (cycles == 15) { + sif::debug << "Setting cold PLOC temperature" << std::endl; + max31865DummyMap[objects::RTD_2_IC5_4K_CAMERA]->setTemperature(-40, true); + } + break; } } cycles++; diff --git a/dummies/TemperatureSensorInserter.h b/dummies/TemperatureSensorInserter.h index eb6cc1ba..9ca3c936 100644 --- a/dummies/TemperatureSensorInserter.h +++ b/dummies/TemperatureSensorInserter.h @@ -12,7 +12,7 @@ class TemperatureSensorInserter : public ExecutableObjectIF, public SystemObject using Max31865DummyMap = std::map; using Tmp1075DummyMap = std::map; explicit TemperatureSensorInserter(object_id_t objectId, Max31865DummyMap tempSensorDummies_, - Tmp1075DummyMap tempTmpSensorDummies_); + std::optional tempTmpSensorDummies_); ReturnValue_t initialize() override; ReturnValue_t initializeAfterTaskCreation() override; @@ -22,7 +22,7 @@ class TemperatureSensorInserter : public ExecutableObjectIF, public SystemObject private: Max31865DummyMap max31865DummyMap; - Tmp1075DummyMap tmp1075DummyMap; + std::optional tmp1075DummyMap; enum TestCase { NONE = 0, @@ -32,6 +32,9 @@ class TemperatureSensorInserter : public ExecutableObjectIF, public SystemObject COLD_STR = 4, COLD_STR_CONSECUTIVE = 5, COLD_CAMERA = 6, + COLD_PLOC_CONSECUTIVE = 7, + COLD_PLOC_STAYS_COLD = 8, + COLD_CAMERA_STAYS_COLD = 9 }; int iteration = 0; uint32_t cycles = 0; diff --git a/dummies/Tmp1075Dummy.cpp b/dummies/Tmp1075Dummy.cpp index 91a50774..7e61acef 100644 --- a/dummies/Tmp1075Dummy.cpp +++ b/dummies/Tmp1075Dummy.cpp @@ -8,35 +8,57 @@ using namespace returnvalue; Tmp1075Dummy::Tmp1075Dummy(object_id_t objectId, object_id_t comif, CookieIF *comCookie) : DeviceHandlerBase(objectId, comif, comCookie), set(this) {} -void Tmp1075Dummy::doStartUp() { setMode(MODE_NORMAL); } -void Tmp1075Dummy::doShutDown() { setMode(MODE_OFF); } +void Tmp1075Dummy::doStartUp() { setMode(MODE_ON); } +void Tmp1075Dummy::doShutDown() { + PoolReadGuard pg(&set); + set.setValidity(false, true); + setMode(MODE_OFF); +} ReturnValue_t Tmp1075Dummy::buildNormalDeviceCommand(DeviceCommandId_t *id) { return NOTHING_TO_SEND; } -ReturnValue_t Tmp1075Dummy::buildTransitionDeviceCommand(DeviceCommandId_t *id) { return OK; } + +ReturnValue_t Tmp1075Dummy::buildTransitionDeviceCommand(DeviceCommandId_t *id) { + return NOTHING_TO_SEND; +} + ReturnValue_t Tmp1075Dummy::buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData, size_t commandDataLen) { - return 0; + return NOTHING_TO_SEND; } + ReturnValue_t Tmp1075Dummy::scanForReply(const uint8_t *start, size_t len, DeviceCommandId_t *foundId, size_t *foundLen) { return 0; } + ReturnValue_t Tmp1075Dummy::interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) { return 0; } + void Tmp1075Dummy::setTemperature(float temperature, bool valid) { PoolReadGuard pg(&set); set.temperatureCelcius.value = temperature; set.setValidity(valid, true); } + void Tmp1075Dummy::fillCommandAndReplyMap() {} + uint32_t Tmp1075Dummy::getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) { return 1000; } + ReturnValue_t Tmp1075Dummy::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { localDataPoolMap.emplace(TMP1075::TEMPERATURE_C_TMP1075, new PoolEntry({10.0}, true)); return OK; } + +ReturnValue_t Tmp1075Dummy::setHealth(HealthState health) { + if (health == FAULTY or health == PERMANENT_FAULTY) { + setMode(_MODE_SHUT_DOWN); + } + return DeviceHandlerBase::setHealth(health); +} + LocalPoolDataSetBase *Tmp1075Dummy::getDataSetHandle(sid_t sid) { return &set; } diff --git a/dummies/Tmp1075Dummy.h b/dummies/Tmp1075Dummy.h index 570fcd42..feab4f98 100644 --- a/dummies/Tmp1075Dummy.h +++ b/dummies/Tmp1075Dummy.h @@ -26,6 +26,7 @@ class Tmp1075Dummy : public DeviceHandlerBase { uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) override; ReturnValue_t initializeLocalDataPool(localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) override; + ReturnValue_t setHealth(HealthState health) override; protected: LocalPoolDataSetBase *getDataSetHandle(sid_t sid) override; diff --git a/dummies/helperFactory.cpp b/dummies/helperFactory.cpp index 157d11d3..b23720b4 100644 --- a/dummies/helperFactory.cpp +++ b/dummies/helperFactory.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -34,6 +35,7 @@ #include "TemperatureSensorInserter.h" #include "dummies/Max31865Dummy.h" +#include "dummies/SusDummy.h" #include "dummies/Tmp1075Dummy.h" #include "mission/genericFactory.h" #include "mission/system/acs/acsModeTree.h" @@ -63,11 +65,14 @@ void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpio rws[3] = new RwDummy(objects::RW4, objects::DUMMY_COM_IF, comCookieDummy); ObjectFactory::createRwAssy(pwrSwitcher, power::Switches::PDU2_CH2_RW_5V, rws, rwIds); new SaDeplDummy(objects::SOLAR_ARRAY_DEPL_HANDLER); - auto* strAssy = new StrAssembly(objects::STR_ASSY); - strAssy->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM); - auto* strDummy = - new StarTrackerDummy(objects::STAR_TRACKER, objects::DUMMY_COM_IF, comCookieDummy); - strDummy->connectModeTreeParent(*strAssy); + + if (cfg.addStrDummy) { + auto* strAssy = new StrAssembly(objects::STR_ASSY); + strAssy->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM); + auto* strDummy = + new StarTrackerDummy(objects::STAR_TRACKER, objects::DUMMY_COM_IF, comCookieDummy); + strDummy->connectModeTreeParent(*strAssy); + } if (cfg.addSyrlinksDummies) { auto* syrlinksDummy = new SyrlinksDummy(objects::SYRLINKS_HANDLER, objects::DUMMY_COM_IF, comCookieDummy); @@ -191,25 +196,36 @@ void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpio objects::RTD_15_IC18_IMTQ, new Max31865Dummy(objects::RTD_15_IC18_IMTQ, objects::DUMMY_COM_IF, comCookieDummy)); - std::map tmpSensorDummies; - tmpSensorDummies.emplace( - objects::TMP1075_HANDLER_TCS_0, - new Tmp1075Dummy(objects::TMP1075_HANDLER_TCS_0, objects::DUMMY_COM_IF, comCookieDummy)); - tmpSensorDummies.emplace( - objects::TMP1075_HANDLER_TCS_1, - new Tmp1075Dummy(objects::TMP1075_HANDLER_TCS_1, objects::DUMMY_COM_IF, comCookieDummy)); - tmpSensorDummies.emplace( - objects::TMP1075_HANDLER_PLPCDU_0, - new Tmp1075Dummy(objects::TMP1075_HANDLER_PLPCDU_0, objects::DUMMY_COM_IF, comCookieDummy)); - // damaged. - // tmpSensorDummies.emplace( - // objects::TMP1075_HANDLER_PLPCDU_1, - // new Tmp1075Dummy(objects::TMP1075_HANDLER_PLPCDU_1, objects::DUMMY_COM_IF, - // comCookieDummy)); - tmpSensorDummies.emplace( - objects::TMP1075_HANDLER_IF_BOARD, - new Tmp1075Dummy(objects::TMP1075_HANDLER_IF_BOARD, objects::DUMMY_COM_IF, comCookieDummy)); - + std::optional tmpSensorDummies; + if (cfg.addTmpDummies) { + TemperatureSensorInserter::Tmp1075DummyMap tmpDummyMap; + if (cfg.tmp1075Cfg.addTcsBrd0) { + tmpDummyMap.emplace(objects::TMP1075_HANDLER_TCS_0, + new Tmp1075Dummy(objects::TMP1075_HANDLER_TCS_0, objects::DUMMY_COM_IF, + comCookieDummy)); + } + if (cfg.tmp1075Cfg.addTcsBrd1) { + tmpDummyMap.emplace(objects::TMP1075_HANDLER_TCS_1, + new Tmp1075Dummy(objects::TMP1075_HANDLER_TCS_1, objects::DUMMY_COM_IF, + comCookieDummy)); + } + if (cfg.tmp1075Cfg.addPlPcdu0) { + tmpDummyMap.emplace(objects::TMP1075_HANDLER_PLPCDU_0, + new Tmp1075Dummy(objects::TMP1075_HANDLER_PLPCDU_0, + objects::DUMMY_COM_IF, comCookieDummy)); + } + if (cfg.tmp1075Cfg.addPlPcdu1) { + tmpDummyMap.emplace(objects::TMP1075_HANDLER_PLPCDU_1, + new Tmp1075Dummy(objects::TMP1075_HANDLER_PLPCDU_1, + objects::DUMMY_COM_IF, comCookieDummy)); + } + if (cfg.tmp1075Cfg.addIfBrd) { + tmpDummyMap.emplace(objects::TMP1075_HANDLER_IF_BOARD, + new Tmp1075Dummy(objects::TMP1075_HANDLER_IF_BOARD, + objects::DUMMY_COM_IF, comCookieDummy)); + } + tmpSensorDummies = std::move(tmpDummyMap); + } new TemperatureSensorInserter(objects::THERMAL_TEMP_INSERTER, rtdSensorDummies, tmpSensorDummies); TcsBoardAssembly* tcsBoardAssy = @@ -217,8 +233,10 @@ void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpio for (auto& rtd : rtdSensorDummies) { rtd.second->connectModeTreeParent(*tcsBoardAssy); } - for (auto& tmp : tmpSensorDummies) { - tmp.second->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); + if (tmpSensorDummies.has_value()) { + for (auto& tmp : tmpSensorDummies.value()) { + tmp.second->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); + } } } if (cfg.addCamSwitcherDummy) { @@ -226,11 +244,15 @@ void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpio power::Switches::PDU2_CH8_PAYLOAD_CAMERA); camSwitcher->connectModeTreeParent(satsystem::payload::SUBSYSTEM); } - auto* scexDummy = new ScexDummy(objects::SCEX, objects::DUMMY_COM_IF, comCookieDummy); - scexDummy->connectModeTreeParent(satsystem::payload::SUBSYSTEM); - auto* plPcduDummy = - new PlPcduDummy(objects::PLPCDU_HANDLER, objects::DUMMY_COM_IF, comCookieDummy); - plPcduDummy->connectModeTreeParent(satsystem::payload::SUBSYSTEM); + if (cfg.addScexDummy) { + auto* scexDummy = new ScexDummy(objects::SCEX, objects::DUMMY_COM_IF, comCookieDummy); + scexDummy->connectModeTreeParent(satsystem::payload::SUBSYSTEM); + } + if (cfg.addPlPcduDummy) { + auto* plPcduDummy = + new PlPcduDummy(objects::PLPCDU_HANDLER, objects::DUMMY_COM_IF, comCookieDummy); + plPcduDummy->connectModeTreeParent(satsystem::payload::SUBSYSTEM); + } if (cfg.addPlocDummies) { auto* plocMpsocDummy = new PlocMpsocDummy(objects::PLOC_MPSOC_HANDLER, objects::DUMMY_COM_IF, comCookieDummy); @@ -239,4 +261,9 @@ void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpio objects::PLOC_SUPERVISOR_HANDLER, objects::DUMMY_COM_IF, comCookieDummy, pwrSwitcher); plocSupervisorDummy->connectModeTreeParent(satsystem::payload::SUBSYSTEM); } + if (cfg.addRadSensorDummy) { + auto* radSensorDummy = + new RadSensorDummy(objects::RAD_SENSOR, objects::DUMMY_COM_IF, comCookieDummy); + radSensorDummy->connectModeTreeParent(satsystem::payload::SUBSYSTEM); + } } diff --git a/dummies/helperFactory.h b/dummies/helperFactory.h index bab9d8d8..34d167f0 100644 --- a/dummies/helperFactory.h +++ b/dummies/helperFactory.h @@ -6,6 +6,14 @@ class GpioIF; namespace dummy { +struct Tmp1075Cfg { + bool addTcsBrd0 = true; + bool addTcsBrd1 = true; + bool addPlPcdu0 = true; + bool addPlPcdu1 = true; + bool addIfBrd = true; +}; + // Default values targeted towards EM. struct DummyCfg { bool addCoreCtrlCfg = true; @@ -19,7 +27,13 @@ struct DummyCfg { bool addTempSensorDummies = true; bool addRtdComIFDummy = true; bool addPlocDummies = true; + bool addStrDummy = true; + bool addTmpDummies = true; + bool addRadSensorDummy = true; + bool addPlPcduDummy = false; + Tmp1075Cfg tmp1075Cfg; bool addCamSwitcherDummy = false; + bool addScexDummy = false; }; void createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitch, GpioIF* gpioIF, bool enableHkSets); diff --git a/fsfw b/fsfw index 0a977ea6..d575da85 160000 --- a/fsfw +++ b/fsfw @@ -1 +1 @@ -Subproject commit 0a977ea688cd78585aabb9ba511eaf8030452712 +Subproject commit d575da85407e029dabecaffa5368f0c9f1034941 diff --git a/generators/bsp_hosted_events.csv b/generators/bsp_hosted_events.csv index e6a320b8..99ea0670 100644 --- a/generators/bsp_hosted_events.csv +++ b/generators/bsp_hosted_events.csv @@ -133,6 +133,7 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 11802;0x2e1a;RESET_OCCURED;LOW;No description;mission/acs/rwHelpers.h 11901;0x2e7d;BOOTING_FIRMWARE_FAILED_EVENT;LOW;Failed to boot firmware;mission/acs/str/StarTrackerHandler.h 11902;0x2e7e;BOOTING_BOOTLOADER_FAILED_EVENT;LOW;Failed to boot star tracker into bootloader mode;mission/acs/str/StarTrackerHandler.h +11903;0x2e7f;COM_ERROR_REPLY_RECEIVED;LOW;Received COM error. P1: Communication Error ID (datasheet p32);mission/acs/str/StarTrackerHandler.h 12001;0x2ee1;SUPV_MEMORY_READ_RPT_CRC_FAILURE;LOW;PLOC supervisor crc failure in telemetry packet;linux/payload/PlocSupervisorHandler.h 12002;0x2ee2;SUPV_UNKNOWN_TM;LOW;Unhandled event. P1: APID, P2: Service ID;linux/payload/PlocSupervisorHandler.h 12003;0x2ee3;SUPV_UNINIMPLEMENTED_TM;LOW;No description;linux/payload/PlocSupervisorHandler.h @@ -254,6 +255,7 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 13800;0x35e8;MISSING_PACKET;LOW;No description;mission/payload/scexHelpers.h 13801;0x35e9;EXPERIMENT_TIMEDOUT;LOW;No description;mission/payload/scexHelpers.h 13802;0x35ea;MULTI_PACKET_COMMAND_DONE;INFO;No description;mission/payload/scexHelpers.h +13803;0x35eb;FS_UNUSABLE;LOW;No description;mission/payload/scexHelpers.h 13901;0x364d;SET_CONFIGFILEVALUE_FAILED;MEDIUM;No description;mission/utility/GlobalConfigHandler.h 13902;0x364e;GET_CONFIGFILEVALUE_FAILED;MEDIUM;No description;mission/utility/GlobalConfigHandler.h 13903;0x364f;INSERT_CONFIGFILEVALUE_FAILED;MEDIUM;No description;mission/utility/GlobalConfigHandler.h @@ -271,6 +273,8 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 14010;0x36ba;TRYING_I2C_RECOVERY;HIGH;I2C is unavailable. Trying recovery of I2C bus by power cycling all I2C devices.;mission/sysDefs.h 14011;0x36bb;I2C_REBOOT;HIGH;I2C is unavailable. Recovery did not work, performing full reboot.;mission/sysDefs.h 14012;0x36bc;PDEC_REBOOT;HIGH;PDEC recovery through reset was not possible, performing full reboot.;mission/sysDefs.h +14013;0x36bd;FIRMWARE_INFO;INFO;Version information of the firmware (not OBSW). 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.;mission/sysDefs.h +14014;0x36be;ACTIVE_SD_INFO;INFO;Active SD card info. SD States: 0: OFF, 1: ON, 2: MOUNTED. P1: Active SD Card Index, 0 if none is active P2: First two bytes: SD state of SD card 0, last two bytes SD state of SD card 1;mission/sysDefs.h 14100;0x3714;NO_VALID_SENSOR_TEMPERATURE;MEDIUM;No description;mission/controller/tcsDefs.h 14101;0x3715;NO_HEALTHY_HEATER_AVAILABLE;MEDIUM;No description;mission/controller/tcsDefs.h 14102;0x3716;SYRLINKS_OVERHEATING;HIGH;No description;mission/controller/tcsDefs.h @@ -279,6 +283,9 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 14106;0x371a;PCDU_SYSTEM_OVERHEATING;HIGH;No description;mission/controller/tcsDefs.h 14107;0x371b;HEATER_NOT_OFF_FOR_OFF_MODE;MEDIUM;No description;mission/controller/tcsDefs.h 14108;0x371c;MGT_OVERHEATING;HIGH;No description;mission/controller/tcsDefs.h +14109;0x371d;TCS_SWITCHING_HEATER_ON;INFO;P1: Module index. P2: Heater index;mission/controller/tcsDefs.h +14110;0x371e;TCS_SWITCHING_HEATER_OFF;INFO;P1: Module index. P2: Heater index;mission/controller/tcsDefs.h +14111;0x371f;TCS_HEATER_MAX_BURN_TIME_REACHED;MEDIUM;P1: Heater index. P2: Maximum burn time for heater.;mission/controller/tcsDefs.h 14201;0x3779;TX_TIMER_EXPIRED;INFO;The transmit timer to protect the Syrlinks expired P1: The current timer value;mission/system/com/ComSubsystem.h 14202;0x377a;BIT_LOCK_TX_ON;INFO;Transmitter will be turned on due to detection of bitlock;mission/system/com/ComSubsystem.h 14300;0x37dc;POSSIBLE_FILE_CORRUPTION;LOW;P1: Result code of TM packet parser. P2: Timestamp of possibly corrupt file as a unix timestamp.;mission/persistentTmStoreDefs.h diff --git a/generators/bsp_q7s_events.csv b/generators/bsp_q7s_events.csv index e6a320b8..99ea0670 100644 --- a/generators/bsp_q7s_events.csv +++ b/generators/bsp_q7s_events.csv @@ -133,6 +133,7 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 11802;0x2e1a;RESET_OCCURED;LOW;No description;mission/acs/rwHelpers.h 11901;0x2e7d;BOOTING_FIRMWARE_FAILED_EVENT;LOW;Failed to boot firmware;mission/acs/str/StarTrackerHandler.h 11902;0x2e7e;BOOTING_BOOTLOADER_FAILED_EVENT;LOW;Failed to boot star tracker into bootloader mode;mission/acs/str/StarTrackerHandler.h +11903;0x2e7f;COM_ERROR_REPLY_RECEIVED;LOW;Received COM error. P1: Communication Error ID (datasheet p32);mission/acs/str/StarTrackerHandler.h 12001;0x2ee1;SUPV_MEMORY_READ_RPT_CRC_FAILURE;LOW;PLOC supervisor crc failure in telemetry packet;linux/payload/PlocSupervisorHandler.h 12002;0x2ee2;SUPV_UNKNOWN_TM;LOW;Unhandled event. P1: APID, P2: Service ID;linux/payload/PlocSupervisorHandler.h 12003;0x2ee3;SUPV_UNINIMPLEMENTED_TM;LOW;No description;linux/payload/PlocSupervisorHandler.h @@ -254,6 +255,7 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 13800;0x35e8;MISSING_PACKET;LOW;No description;mission/payload/scexHelpers.h 13801;0x35e9;EXPERIMENT_TIMEDOUT;LOW;No description;mission/payload/scexHelpers.h 13802;0x35ea;MULTI_PACKET_COMMAND_DONE;INFO;No description;mission/payload/scexHelpers.h +13803;0x35eb;FS_UNUSABLE;LOW;No description;mission/payload/scexHelpers.h 13901;0x364d;SET_CONFIGFILEVALUE_FAILED;MEDIUM;No description;mission/utility/GlobalConfigHandler.h 13902;0x364e;GET_CONFIGFILEVALUE_FAILED;MEDIUM;No description;mission/utility/GlobalConfigHandler.h 13903;0x364f;INSERT_CONFIGFILEVALUE_FAILED;MEDIUM;No description;mission/utility/GlobalConfigHandler.h @@ -271,6 +273,8 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 14010;0x36ba;TRYING_I2C_RECOVERY;HIGH;I2C is unavailable. Trying recovery of I2C bus by power cycling all I2C devices.;mission/sysDefs.h 14011;0x36bb;I2C_REBOOT;HIGH;I2C is unavailable. Recovery did not work, performing full reboot.;mission/sysDefs.h 14012;0x36bc;PDEC_REBOOT;HIGH;PDEC recovery through reset was not possible, performing full reboot.;mission/sysDefs.h +14013;0x36bd;FIRMWARE_INFO;INFO;Version information of the firmware (not OBSW). 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.;mission/sysDefs.h +14014;0x36be;ACTIVE_SD_INFO;INFO;Active SD card info. SD States: 0: OFF, 1: ON, 2: MOUNTED. P1: Active SD Card Index, 0 if none is active P2: First two bytes: SD state of SD card 0, last two bytes SD state of SD card 1;mission/sysDefs.h 14100;0x3714;NO_VALID_SENSOR_TEMPERATURE;MEDIUM;No description;mission/controller/tcsDefs.h 14101;0x3715;NO_HEALTHY_HEATER_AVAILABLE;MEDIUM;No description;mission/controller/tcsDefs.h 14102;0x3716;SYRLINKS_OVERHEATING;HIGH;No description;mission/controller/tcsDefs.h @@ -279,6 +283,9 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 14106;0x371a;PCDU_SYSTEM_OVERHEATING;HIGH;No description;mission/controller/tcsDefs.h 14107;0x371b;HEATER_NOT_OFF_FOR_OFF_MODE;MEDIUM;No description;mission/controller/tcsDefs.h 14108;0x371c;MGT_OVERHEATING;HIGH;No description;mission/controller/tcsDefs.h +14109;0x371d;TCS_SWITCHING_HEATER_ON;INFO;P1: Module index. P2: Heater index;mission/controller/tcsDefs.h +14110;0x371e;TCS_SWITCHING_HEATER_OFF;INFO;P1: Module index. P2: Heater index;mission/controller/tcsDefs.h +14111;0x371f;TCS_HEATER_MAX_BURN_TIME_REACHED;MEDIUM;P1: Heater index. P2: Maximum burn time for heater.;mission/controller/tcsDefs.h 14201;0x3779;TX_TIMER_EXPIRED;INFO;The transmit timer to protect the Syrlinks expired P1: The current timer value;mission/system/com/ComSubsystem.h 14202;0x377a;BIT_LOCK_TX_ON;INFO;Transmitter will be turned on due to detection of bitlock;mission/system/com/ComSubsystem.h 14300;0x37dc;POSSIBLE_FILE_CORRUPTION;LOW;P1: Result code of TM packet parser. P2: Timestamp of possibly corrupt file as a unix timestamp.;mission/persistentTmStoreDefs.h diff --git a/generators/events/event_parser.py b/generators/events/event_parser.py index 94c1580c..f7463cf9 100644 --- a/generators/events/event_parser.py +++ b/generators/events/event_parser.py @@ -54,9 +54,13 @@ class BspConfig: # Store this file in the root of the generators folder self.csv_filename = Path(f"{ROOT_DIR}/{self.bsp_dir_name}_events.csv") - self.subsystems_csv_filename = Path(f"{ROOT_DIR}/{self.bsp_dir_name}_subsystems.csv") + self.subsystems_csv_filename = Path( + f"{ROOT_DIR}/{self.bsp_dir_name}_subsystems.csv" + ) self.csv_copy_dest = Path(f"{OBSW_ROOT_DIR}/tmtc/eive_tmtc/config/events.csv") - self.subsystem_csv_copy_dest = Path(f"{OBSW_ROOT_DIR}/tmtc/eive_tmtc/config/subsystems.csv") + self.subsystem_csv_copy_dest = Path( + f"{OBSW_ROOT_DIR}/tmtc/eive_tmtc/config/subsystems.csv" + ) if ( self.bsp_select == BspType.BSP_Q7S diff --git a/generators/events/translateEvents.cpp b/generators/events/translateEvents.cpp index 79a828f2..98c5035f 100644 --- a/generators/events/translateEvents.cpp +++ b/generators/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** - * @brief Auto-generated event translation file. Contains 296 translations. + * @brief Auto-generated event translation file. Contains 303 translations. * @details - * Generated on: 2023-06-13 07:54:32 + * Generated on: 2023-08-03 13:36:37 */ #include "translateEvents.h" @@ -139,6 +139,7 @@ const char *ERROR_STATE_STRING = "ERROR_STATE"; const char *RESET_OCCURED_STRING = "RESET_OCCURED"; const char *BOOTING_FIRMWARE_FAILED_EVENT_STRING = "BOOTING_FIRMWARE_FAILED_EVENT"; const char *BOOTING_BOOTLOADER_FAILED_EVENT_STRING = "BOOTING_BOOTLOADER_FAILED_EVENT"; +const char *COM_ERROR_REPLY_RECEIVED_STRING = "COM_ERROR_REPLY_RECEIVED"; const char *SUPV_MEMORY_READ_RPT_CRC_FAILURE_STRING = "SUPV_MEMORY_READ_RPT_CRC_FAILURE"; const char *SUPV_UNKNOWN_TM_STRING = "SUPV_UNKNOWN_TM"; const char *SUPV_UNINIMPLEMENTED_TM_STRING = "SUPV_UNINIMPLEMENTED_TM"; @@ -260,6 +261,7 @@ const char *TX_OFF_STRING = "TX_OFF"; const char *MISSING_PACKET_STRING = "MISSING_PACKET"; const char *EXPERIMENT_TIMEDOUT_STRING = "EXPERIMENT_TIMEDOUT"; const char *MULTI_PACKET_COMMAND_DONE_STRING = "MULTI_PACKET_COMMAND_DONE"; +const char *FS_UNUSABLE_STRING = "FS_UNUSABLE"; const char *SET_CONFIGFILEVALUE_FAILED_STRING = "SET_CONFIGFILEVALUE_FAILED"; const char *GET_CONFIGFILEVALUE_FAILED_STRING = "GET_CONFIGFILEVALUE_FAILED"; const char *INSERT_CONFIGFILEVALUE_FAILED_STRING = "INSERT_CONFIGFILEVALUE_FAILED"; @@ -277,6 +279,8 @@ const char *INDIVIDUAL_BOOT_COUNTS_STRING = "INDIVIDUAL_BOOT_COUNTS"; const char *TRYING_I2C_RECOVERY_STRING = "TRYING_I2C_RECOVERY"; const char *I2C_REBOOT_STRING = "I2C_REBOOT"; const char *PDEC_REBOOT_STRING = "PDEC_REBOOT"; +const char *FIRMWARE_INFO_STRING = "FIRMWARE_INFO"; +const char *ACTIVE_SD_INFO_STRING = "ACTIVE_SD_INFO"; 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"; @@ -285,6 +289,9 @@ const char *CAMERA_OVERHEATING_STRING = "CAMERA_OVERHEATING"; const char *PCDU_SYSTEM_OVERHEATING_STRING = "PCDU_SYSTEM_OVERHEATING"; const char *HEATER_NOT_OFF_FOR_OFF_MODE_STRING = "HEATER_NOT_OFF_FOR_OFF_MODE"; const char *MGT_OVERHEATING_STRING = "MGT_OVERHEATING"; +const char *TCS_SWITCHING_HEATER_ON_STRING = "TCS_SWITCHING_HEATER_ON"; +const char *TCS_SWITCHING_HEATER_OFF_STRING = "TCS_SWITCHING_HEATER_OFF"; +const char *TCS_HEATER_MAX_BURN_TIME_REACHED_STRING = "TCS_HEATER_MAX_BURN_TIME_REACHED"; const char *TX_TIMER_EXPIRED_STRING = "TX_TIMER_EXPIRED"; const char *BIT_LOCK_TX_ON_STRING = "BIT_LOCK_TX_ON"; const char *POSSIBLE_FILE_CORRUPTION_STRING = "POSSIBLE_FILE_CORRUPTION"; @@ -572,6 +579,8 @@ const char *translateEvents(Event event) { return BOOTING_FIRMWARE_FAILED_EVENT_STRING; case (11902): return BOOTING_BOOTLOADER_FAILED_EVENT_STRING; + case (11903): + return COM_ERROR_REPLY_RECEIVED_STRING; case (12001): return SUPV_MEMORY_READ_RPT_CRC_FAILURE_STRING; case (12002): @@ -814,6 +823,8 @@ const char *translateEvents(Event event) { return EXPERIMENT_TIMEDOUT_STRING; case (13802): return MULTI_PACKET_COMMAND_DONE_STRING; + case (13803): + return FS_UNUSABLE_STRING; case (13901): return SET_CONFIGFILEVALUE_FAILED_STRING; case (13902): @@ -848,6 +859,10 @@ const char *translateEvents(Event event) { return I2C_REBOOT_STRING; case (14012): return PDEC_REBOOT_STRING; + case (14013): + return FIRMWARE_INFO_STRING; + case (14014): + return ACTIVE_SD_INFO_STRING; case (14100): return NO_VALID_SENSOR_TEMPERATURE_STRING; case (14101): @@ -864,6 +879,12 @@ const char *translateEvents(Event event) { return HEATER_NOT_OFF_FOR_OFF_MODE_STRING; case (14108): return MGT_OVERHEATING_STRING; + case (14109): + return TCS_SWITCHING_HEATER_ON_STRING; + case (14110): + return TCS_SWITCHING_HEATER_OFF_STRING; + case (14111): + return TCS_HEATER_MAX_BURN_TIME_REACHED_STRING; case (14201): return TX_TIMER_EXPIRED_STRING; case (14202): diff --git a/generators/objects/translateObjects.cpp b/generators/objects/translateObjects.cpp index f1e7f6c6..ce21a66f 100644 --- a/generators/objects/translateObjects.cpp +++ b/generators/objects/translateObjects.cpp @@ -2,7 +2,7 @@ * @brief Auto-generated object translation file. * @details * Contains 175 translations. - * Generated on: 2023-06-13 07:54:32 + * Generated on: 2023-08-03 13:36:37 */ #include "translateObjects.h" diff --git a/linux/acs/StrComHandler.cpp b/linux/acs/StrComHandler.cpp index 5ae00c94..2db73f35 100644 --- a/linux/acs/StrComHandler.cpp +++ b/linux/acs/StrComHandler.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -23,6 +24,8 @@ extern "C" { using namespace returnvalue; +static constexpr bool PACKET_WIRETAPPING = false; + StrComHandler::StrComHandler(object_id_t objectId) : SystemObject(objectId) { lock = MutexFactory::instance()->createMutex(); semaphore.acquire(); @@ -52,7 +55,7 @@ ReturnValue_t StrComHandler::performOperation(uint8_t operationCode) { case InternalState::POLL_ONE_REPLY: { // Stopwatch watch; replyTimeout.setTimeout(200); - replyResult = readOneReply(static_cast(state)); + readOneReply(static_cast(state)); { MutexGuard mg(lock); replyWasReceived = true; @@ -680,6 +683,10 @@ ReturnValue_t StrComHandler::sendMessage(CookieIF* cookie, const uint8_t* sendDa const uint8_t* txFrame; size_t frameLen; datalinkLayer.encodeFrame(sendData, sendLen, &txFrame, frameLen); + if (PACKET_WIRETAPPING) { + sif::debug << "Sending STR frame" << std::endl; + arrayprinter::print(txFrame, frameLen); + } ssize_t bytesWritten = write(serialPort, txFrame, frameLen); if (bytesWritten != static_cast(frameLen)) { sif::warning << "StrComHandler: Sending packet failed" << std::endl; @@ -709,13 +716,11 @@ ReturnValue_t StrComHandler::requestReceiveMessage(CookieIF* cookie, size_t requ } ReturnValue_t StrComHandler::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, size_t* size) { - // Consider it a configuration error if the task is not done with a command -> reply cycle - // in time. bool replyWasReceived = false; { MutexGuard mg(lock); if (state != InternalState::SLEEPING) { - return BUSY; + return returnvalue::OK; } replyWasReceived = this->replyWasReceived; } @@ -728,7 +733,7 @@ ReturnValue_t StrComHandler::readReceivedMessage(CookieIF* cookie, uint8_t** buf *size = replyLen; } replyLen = 0; - return replyResult; + return returnvalue::OK; } ReturnValue_t StrComHandler::unlockAndEraseRegions(uint32_t from, uint32_t to) { @@ -782,8 +787,10 @@ ReturnValue_t StrComHandler::handleSerialReception() { << std::endl; return FAILED; } else if (bytesRead > 0) { - // sif::info << "Received " << bytesRead << " bytes from the STR" << std::endl; - // arrayprinter::print(recBuf.data(), bytesRead); + if (PACKET_WIRETAPPING) { + sif::info << "Received " << bytesRead << " bytes from the STR" << std::endl; + arrayprinter::print(recBuf.data(), bytesRead); + } datalinkLayer.feedData(recBuf.data(), bytesRead); } return OK; @@ -797,6 +804,10 @@ ReturnValue_t StrComHandler::readOneReply(uint32_t failParameter) { handleSerialReception(); result = datalinkLayer.checkRingBufForFrame(&replyPtr, replyLen); if (result == returnvalue::OK) { + if (PACKET_WIRETAPPING) { + sif::debug << "Received STR reply frame" << std::endl; + arrayprinter::print(replyPtr, replyLen); + } return returnvalue::OK; } else if (result != ArcsecDatalinkLayer::DEC_IN_PROGRESS) { triggerEvent(STR_HELPER_DEC_ERROR, result, failParameter); diff --git a/linux/fsfwconfig/events/translateEvents.cpp b/linux/fsfwconfig/events/translateEvents.cpp index 79a828f2..98c5035f 100644 --- a/linux/fsfwconfig/events/translateEvents.cpp +++ b/linux/fsfwconfig/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** - * @brief Auto-generated event translation file. Contains 296 translations. + * @brief Auto-generated event translation file. Contains 303 translations. * @details - * Generated on: 2023-06-13 07:54:32 + * Generated on: 2023-08-03 13:36:37 */ #include "translateEvents.h" @@ -139,6 +139,7 @@ const char *ERROR_STATE_STRING = "ERROR_STATE"; const char *RESET_OCCURED_STRING = "RESET_OCCURED"; const char *BOOTING_FIRMWARE_FAILED_EVENT_STRING = "BOOTING_FIRMWARE_FAILED_EVENT"; const char *BOOTING_BOOTLOADER_FAILED_EVENT_STRING = "BOOTING_BOOTLOADER_FAILED_EVENT"; +const char *COM_ERROR_REPLY_RECEIVED_STRING = "COM_ERROR_REPLY_RECEIVED"; const char *SUPV_MEMORY_READ_RPT_CRC_FAILURE_STRING = "SUPV_MEMORY_READ_RPT_CRC_FAILURE"; const char *SUPV_UNKNOWN_TM_STRING = "SUPV_UNKNOWN_TM"; const char *SUPV_UNINIMPLEMENTED_TM_STRING = "SUPV_UNINIMPLEMENTED_TM"; @@ -260,6 +261,7 @@ const char *TX_OFF_STRING = "TX_OFF"; const char *MISSING_PACKET_STRING = "MISSING_PACKET"; const char *EXPERIMENT_TIMEDOUT_STRING = "EXPERIMENT_TIMEDOUT"; const char *MULTI_PACKET_COMMAND_DONE_STRING = "MULTI_PACKET_COMMAND_DONE"; +const char *FS_UNUSABLE_STRING = "FS_UNUSABLE"; const char *SET_CONFIGFILEVALUE_FAILED_STRING = "SET_CONFIGFILEVALUE_FAILED"; const char *GET_CONFIGFILEVALUE_FAILED_STRING = "GET_CONFIGFILEVALUE_FAILED"; const char *INSERT_CONFIGFILEVALUE_FAILED_STRING = "INSERT_CONFIGFILEVALUE_FAILED"; @@ -277,6 +279,8 @@ const char *INDIVIDUAL_BOOT_COUNTS_STRING = "INDIVIDUAL_BOOT_COUNTS"; const char *TRYING_I2C_RECOVERY_STRING = "TRYING_I2C_RECOVERY"; const char *I2C_REBOOT_STRING = "I2C_REBOOT"; const char *PDEC_REBOOT_STRING = "PDEC_REBOOT"; +const char *FIRMWARE_INFO_STRING = "FIRMWARE_INFO"; +const char *ACTIVE_SD_INFO_STRING = "ACTIVE_SD_INFO"; 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"; @@ -285,6 +289,9 @@ const char *CAMERA_OVERHEATING_STRING = "CAMERA_OVERHEATING"; const char *PCDU_SYSTEM_OVERHEATING_STRING = "PCDU_SYSTEM_OVERHEATING"; const char *HEATER_NOT_OFF_FOR_OFF_MODE_STRING = "HEATER_NOT_OFF_FOR_OFF_MODE"; const char *MGT_OVERHEATING_STRING = "MGT_OVERHEATING"; +const char *TCS_SWITCHING_HEATER_ON_STRING = "TCS_SWITCHING_HEATER_ON"; +const char *TCS_SWITCHING_HEATER_OFF_STRING = "TCS_SWITCHING_HEATER_OFF"; +const char *TCS_HEATER_MAX_BURN_TIME_REACHED_STRING = "TCS_HEATER_MAX_BURN_TIME_REACHED"; const char *TX_TIMER_EXPIRED_STRING = "TX_TIMER_EXPIRED"; const char *BIT_LOCK_TX_ON_STRING = "BIT_LOCK_TX_ON"; const char *POSSIBLE_FILE_CORRUPTION_STRING = "POSSIBLE_FILE_CORRUPTION"; @@ -572,6 +579,8 @@ const char *translateEvents(Event event) { return BOOTING_FIRMWARE_FAILED_EVENT_STRING; case (11902): return BOOTING_BOOTLOADER_FAILED_EVENT_STRING; + case (11903): + return COM_ERROR_REPLY_RECEIVED_STRING; case (12001): return SUPV_MEMORY_READ_RPT_CRC_FAILURE_STRING; case (12002): @@ -814,6 +823,8 @@ const char *translateEvents(Event event) { return EXPERIMENT_TIMEDOUT_STRING; case (13802): return MULTI_PACKET_COMMAND_DONE_STRING; + case (13803): + return FS_UNUSABLE_STRING; case (13901): return SET_CONFIGFILEVALUE_FAILED_STRING; case (13902): @@ -848,6 +859,10 @@ const char *translateEvents(Event event) { return I2C_REBOOT_STRING; case (14012): return PDEC_REBOOT_STRING; + case (14013): + return FIRMWARE_INFO_STRING; + case (14014): + return ACTIVE_SD_INFO_STRING; case (14100): return NO_VALID_SENSOR_TEMPERATURE_STRING; case (14101): @@ -864,6 +879,12 @@ const char *translateEvents(Event event) { return HEATER_NOT_OFF_FOR_OFF_MODE_STRING; case (14108): return MGT_OVERHEATING_STRING; + case (14109): + return TCS_SWITCHING_HEATER_ON_STRING; + case (14110): + return TCS_SWITCHING_HEATER_OFF_STRING; + case (14111): + return TCS_HEATER_MAX_BURN_TIME_REACHED_STRING; case (14201): return TX_TIMER_EXPIRED_STRING; case (14202): diff --git a/linux/fsfwconfig/objects/translateObjects.cpp b/linux/fsfwconfig/objects/translateObjects.cpp index f1e7f6c6..ce21a66f 100644 --- a/linux/fsfwconfig/objects/translateObjects.cpp +++ b/linux/fsfwconfig/objects/translateObjects.cpp @@ -2,7 +2,7 @@ * @brief Auto-generated object translation file. * @details * Contains 175 translations. - * Generated on: 2023-06-13 07:54:32 + * Generated on: 2023-08-03 13:36:37 */ #include "translateObjects.h" diff --git a/linux/ipcore/AxiPtmeConfig.cpp b/linux/ipcore/AxiPtmeConfig.cpp index 6dee3e2f..b21edf5b 100644 --- a/linux/ipcore/AxiPtmeConfig.cpp +++ b/linux/ipcore/AxiPtmeConfig.cpp @@ -16,9 +16,9 @@ AxiPtmeConfig::AxiPtmeConfig(object_id_t objectId, std::string axiUio, int mapNu AxiPtmeConfig::~AxiPtmeConfig() {} ReturnValue_t AxiPtmeConfig::initialize() { - ReturnValue_t result = returnvalue::OK; UioMapper uioMapper(axiUio, mapNum); - result = uioMapper.getMappedAdress(&baseAddress, UioMapper::Permissions::READ_WRITE); + ReturnValue_t result = + uioMapper.getMappedAdress(&baseAddress, UioMapper::Permissions::READ_WRITE); if (result != returnvalue::OK) { return result; } @@ -26,8 +26,7 @@ ReturnValue_t AxiPtmeConfig::initialize() { } ReturnValue_t AxiPtmeConfig::writeCaduRateReg(uint8_t rateVal) { - ReturnValue_t result = returnvalue::OK; - result = mutex->lockMutex(timeoutType, mutexTimeout); + ReturnValue_t result = mutex->lockMutex(timeoutType, mutexTimeout); if (result != returnvalue::OK) { sif::warning << "AxiPtmeConfig::writeCaduRateReg: Failed to lock mutex" << std::endl; return returnvalue::FAILED; @@ -41,6 +40,11 @@ ReturnValue_t AxiPtmeConfig::writeCaduRateReg(uint8_t rateVal) { return returnvalue::OK; } +uint8_t AxiPtmeConfig::readCaduRateReg() { + MutexGuard mg(mutex); + return static_cast(*(baseAddress + CADU_BITRATE_REG)); +} + void AxiPtmeConfig::enableTxclockManipulator() { writeBit(COMMON_CONFIG_REG, true, BitPos::EN_TX_CLK_MANIPULATOR); } diff --git a/linux/ipcore/AxiPtmeConfig.h b/linux/ipcore/AxiPtmeConfig.h index 98188775..ebdf4d38 100644 --- a/linux/ipcore/AxiPtmeConfig.h +++ b/linux/ipcore/AxiPtmeConfig.h @@ -38,6 +38,7 @@ class AxiPtmeConfig : public SystemObject { * frequency of the clock connected to the bit clock input of PTME. */ ReturnValue_t writeCaduRateReg(uint8_t rateVal); + uint8_t readCaduRateReg(); /** * @brief Next to functions control the tx clock manipulator component diff --git a/linux/ipcore/PapbVcInterface.cpp b/linux/ipcore/PapbVcInterface.cpp index 60968cc6..5dcb4519 100644 --- a/linux/ipcore/PapbVcInterface.cpp +++ b/linux/ipcore/PapbVcInterface.cpp @@ -7,20 +7,16 @@ #include "fsfw/serviceinterface/ServiceInterface.h" -PapbVcInterface::PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbBusyId, - gpioId_t papbEmptyId, std::string uioFile, int mapNum) - : gpioComIF(gpioComIF), - papbBusyId(papbBusyId), - papbEmptyId(papbEmptyId), - uioFile(std::move(uioFile)), - mapNum(mapNum) {} +PapbVcInterface::PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbEmptyId, + std::string uioFile, int mapNum) + : gpioComIF(gpioComIF), papbEmptyId(papbEmptyId), uioFile(std::move(uioFile)), mapNum(mapNum) {} PapbVcInterface::~PapbVcInterface() {} ReturnValue_t PapbVcInterface::initialize() { UioMapper uioMapper(uioFile, mapNum); ReturnValue_t result = uioMapper.getMappedAdress(const_cast(&vcBaseReg), - UioMapper::Permissions::WRITE_ONLY); + UioMapper::Permissions::READ_WRITE); if (result != returnvalue::OK) { return result; } @@ -32,63 +28,27 @@ ReturnValue_t PapbVcInterface::write(const uint8_t* data, size_t size) { if (size < 4) { return returnvalue::FAILED; } - if (pollInterfaceReadiness(0, true) == returnvalue::OK) { + if (pollReadyForPacket()) { startPacketTransfer(ByteWidthCfg::ONE); } else { return DirectTmSinkIF::IS_BUSY; } - // TODO: This should work but does not.. :( - // size_t idx = 0; - // while (idx < size) { - // - // nanosleep(&BETWEEN_POLL_DELAY, &remDelay); - // if ((size - idx) < 4) { - // *vcBaseReg = CONFIG_DATA_INPUT | (size - idx - 1); - // usleep(1); - // } - // if (pollPapbBusySignal(2) == returnvalue::OK) { - // // vcBaseReg + DATA_REG_OFFSET + 3 = static_cast(data + idx); - // // vcBaseReg + DATA_REG_OFFSET + 2 = static_cast(data + idx + 1); - // // vcBaseReg + DATA_REG_OFFSET + 1 = static_cast(data + idx + 2); - // // vcBaseReg + DATA_REG_OFFSET = static_cast(data + idx + 3); - // - // // std::memcpy((vcBaseReg + DATA_REG_OFFSET), data + idx , nextWriteSize); - // *(vcBaseReg + DATA_REG_OFFSET) = *reinterpret_cast(data + idx); - // //uint8_t* byteReg = reinterpret_cast(vcBaseReg + DATA_REG_OFFSET); - // - // //byteReg[0] = data[idx]; - // //byteReg[1] = data[idx]; - // } else { - // abortPacketTransfer(); - // return returnvalue::FAILED; - // } - // // TODO: Change this after the bugfix. Right now, the PAPB ignores the content of the byte - // // width configuration.5 - // // It's okay to increment by a larger amount for the last segment here, loop will be over - // // in any case. - // idx += 4; - // } - for (size_t idx = 0; idx < size; idx++) { - // This delay is super-important, DO NOT REMOVE! - // Polling the GPIO or the config register too often messes up the scheduler. - // TODO: Maybe this should not be done like this. It would be better if there was a custom - // FPGA module which can accept packets and then takes care of dumping that packet into - // the PTME. DMA would be an ideal solution for this. - nanosleep(&BETWEEN_POLL_DELAY, &remDelay); - if (pollInterfaceReadiness(2, false) == returnvalue::OK) { - *(vcBaseReg + DATA_REG_OFFSET) = static_cast(data[idx]); - } else { - abortPacketTransfer(); - return returnvalue::FAILED; - } - } - nanosleep(&BETWEEN_POLL_DELAY, &remDelay); - if (pollInterfaceReadiness(2, false) == returnvalue::OK) { - completePacketTransfer(); - } else { + if (not pollReadyForOctet(MAX_BUSY_POLLS)) { abortPacketTransfer(); return returnvalue::FAILED; } + for (size_t idx = 0; idx < size; idx++) { + if (not pollReadyForOctet(MAX_BUSY_POLLS)) { + abortPacketTransfer(); + return returnvalue::FAILED; + } + *(vcBaseReg + DATA_REG_OFFSET) = static_cast(data[idx]); + } + if (not pollReadyForOctet(MAX_BUSY_POLLS)) { + abortPacketTransfer(); + return returnvalue::FAILED; + } + completePacketTransfer(); return returnvalue::OK; } @@ -98,63 +58,49 @@ void PapbVcInterface::startPacketTransfer(ByteWidthCfg initWidth) { void PapbVcInterface::completePacketTransfer() { *vcBaseReg = CONFIG_END; } -ReturnValue_t PapbVcInterface::pollInterfaceReadiness(uint32_t maxPollRetries, - bool checkReadyState) const { - uint32_t busyIdx = 0; - nextDelay.tv_nsec = FIRST_DELAY_PAPB_POLLING_NS; - - while (true) { - // Check if PAPB interface is ready to receive data. Use the configuration register for this. - // Bit 5, see PTME ptme_001_01-0-7-r2 Table 31. - uint32_t reg = *vcBaseReg; - bool busy = (reg >> 5) & 0b1; - bool ready = (reg >> 6) & 0b1; - if (not busy) { - return returnvalue::OK; - } - if (checkReadyState and not ready) { - return PAPB_BUSY; - } - - busyIdx++; - if (busyIdx >= maxPollRetries) { - return PAPB_BUSY; - } - - // Ignore signal handling here for now. - nanosleep(&nextDelay, &remDelay); - // Adaptive delay. - if (nextDelay.tv_nsec * 2 <= MAX_DELAY_PAPB_POLLING_NS) { - nextDelay.tv_nsec *= 2; - } - } - return returnvalue::OK; +bool PapbVcInterface::pollReadyForPacket() const { + // Check if PAPB interface is ready to receive data. Use the configuration register for this. + // Bit 5, see PTME ptme_001_01-0-7-r2 Table 31. + uint32_t reg = *vcBaseReg; + return (reg >> 6) & 0b1; } -void PapbVcInterface::isVcInterfaceBufferEmpty() { +bool PapbVcInterface::isVcInterfaceBufferEmpty() { ReturnValue_t result = returnvalue::OK; gpio::Levels papbEmptyState = gpio::Levels::HIGH; result = gpioComIF->readGpio(papbEmptyId, papbEmptyState); if (result != returnvalue::OK) { - sif::warning << "PapbVcInterface::isVcInterfaceBufferEmpty: Failed to read papb empty signal" - << std::endl; - return; + sif::error << "PapbVcInterface::isVcInterfaceBufferEmpty: Failed to read papb empty signal" + << std::endl; + return true; } if (papbEmptyState == gpio::Levels::HIGH) { - sif::debug << "PapbVcInterface::isVcInterfaceBufferEmpty: Buffer is empty" << std::endl; - } else { - sif::debug << "PapbVcInterface::isVcInterfaceBufferEmpty: Buffer is not empty" << std::endl; + return true; } - return; + return false; } -bool PapbVcInterface::isBusy() const { return pollInterfaceReadiness(0, true) == PAPB_BUSY; } +bool PapbVcInterface::isBusy() const { return not pollReadyForPacket(); } void PapbVcInterface::cancelTransfer() { abortPacketTransfer(); } +inline bool PapbVcInterface::pollReadyForOctet(uint32_t maxCycles) const { + uint32_t reg; + uint32_t idx = 0; + while (idx < maxCycles) { + reg = *vcBaseReg; + // Busy bit. + if (not((reg >> 5) & 0b1)) { + return true; + } + idx++; + } + return false; +} + ReturnValue_t PapbVcInterface::sendTestFrame() { /** Size of one complete transfer frame data field amounts to 1105 bytes */ uint8_t testPacket[1105]; diff --git a/linux/ipcore/PapbVcInterface.h b/linux/ipcore/PapbVcInterface.h index e54def5d..b5160748 100644 --- a/linux/ipcore/PapbVcInterface.h +++ b/linux/ipcore/PapbVcInterface.h @@ -30,8 +30,7 @@ class PapbVcInterface : public VirtualChannelIF { * @param uioFile UIO file providing access to the PAPB bus * @param mapNum Map number of UIO map associated with this virtual channel */ - PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbBusyId, gpioId_t papbEmptyId, - std::string uioFile, int mapNum); + PapbVcInterface(LinuxLibgpioIF* gpioComIF, gpioId_t papbEmptyId, std::string uioFile, int mapNum); virtual ~PapbVcInterface(); bool isBusy() const override; @@ -81,11 +80,9 @@ class PapbVcInterface : public VirtualChannelIF { static constexpr long int FIRST_DELAY_PAPB_POLLING_NS = 10; static constexpr long int MAX_DELAY_PAPB_POLLING_NS = 40; + static constexpr uint32_t MAX_BUSY_POLLS = 1000; LinuxLibgpioIF* gpioComIF = nullptr; - - /** Pulled to low when virtual channel not ready to receive data */ - gpioId_t papbBusyId = gpio::NO_GPIO; /** High when external buffer memory of virtual channel is empty */ gpioId_t papbEmptyId = gpio::NO_GPIO; @@ -120,13 +117,15 @@ class PapbVcInterface : public VirtualChannelIF { * * @return returnvalue::OK when ready to receive data else PAPB_BUSY. */ - inline ReturnValue_t pollInterfaceReadiness(uint32_t maxPollRetries, bool checkReadyState) const; + inline bool pollReadyForPacket() const; + + inline bool pollReadyForOctet(uint32_t maxCycles) const; /** * @brief This function can be used for debugging to check whether there are packets in * the packet buffer of the virtual channel or not. */ - void isVcInterfaceBufferEmpty(); + bool isVcInterfaceBufferEmpty(); /** * @brief This function sends a complete telemetry transfer frame data field (1105 bytes) diff --git a/linux/ipcore/PtmeConfig.cpp b/linux/ipcore/PtmeConfig.cpp index 5f247b54..5b6b9343 100644 --- a/linux/ipcore/PtmeConfig.cpp +++ b/linux/ipcore/PtmeConfig.cpp @@ -26,6 +26,11 @@ ReturnValue_t PtmeConfig::setRate(uint32_t bitRate) { return axiPtmeConfig->writeCaduRateReg(static_cast(rateVal)); } +uint32_t PtmeConfig::getRate() { + uint8_t rateReg = axiPtmeConfig->readCaduRateReg(); + return (BIT_CLK_FREQ / (rateReg + 1)); +} + void PtmeConfig::invertTxClock(bool invert) { if (invert) { axiPtmeConfig->enableTxclockInversion(); diff --git a/linux/ipcore/PtmeConfig.h b/linux/ipcore/PtmeConfig.h index 87614187..11eeff7d 100644 --- a/linux/ipcore/PtmeConfig.h +++ b/linux/ipcore/PtmeConfig.h @@ -32,6 +32,7 @@ class PtmeConfig : public SystemObject { * of the CADU clock due to the convolutional code added by the s-Band transceiver. */ ReturnValue_t setRate(uint32_t bitRate); + uint32_t getRate(); /** * @brief Will change the time the tx data signal is updated with respect to the tx clock diff --git a/misc/eclipse/.cproject b/misc/eclipse/.cproject index 4cfe3fa1..154cb27e 100644 --- a/misc/eclipse/.cproject +++ b/misc/eclipse/.cproject @@ -57,7 +57,8 @@ - + + @@ -119,7 +120,8 @@ - + + @@ -187,7 +189,8 @@ - + + @@ -255,7 +258,8 @@ - + + @@ -418,7 +422,8 @@ - + + @@ -580,7 +585,8 @@ - + + @@ -750,7 +756,8 @@ - + + @@ -917,7 +924,8 @@ - + + @@ -1084,7 +1092,8 @@ - + + @@ -1149,7 +1158,8 @@ - + + @@ -1172,7 +1182,7 @@ - + - + + + @@ -1386,7 +1398,8 @@ - + + diff --git a/mission/acs/SusHandler.cpp b/mission/acs/SusHandler.cpp index 5fbbca4f..7a9e7d4f 100644 --- a/mission/acs/SusHandler.cpp +++ b/mission/acs/SusHandler.cpp @@ -30,6 +30,7 @@ void SusHandler::doStartUp() { void SusHandler::doShutDown() { if (internalState != InternalState::SHUTDOWN) { PoolReadGuard pg(&dataset); + dataset.tempC = thermal::INVALID_TEMPERATURE; dataset.setValidity(false, true); internalState = InternalState::SHUTDOWN; commandExecuted = false; diff --git a/mission/acs/defs.h b/mission/acs/defs.h index 41d09976..677ce37a 100644 --- a/mission/acs/defs.h +++ b/mission/acs/defs.h @@ -26,10 +26,18 @@ enum SafeModeStrategy : uint8_t { SAFECTRL_OFF = 0, SAFECTRL_NO_MAG_FIELD_FOR_CONTROL = 1, SAFECTRL_NO_SENSORS_FOR_CONTROL = 2, - SAFECTRL_ACTIVE_MEKF = 10, - SAFECTRL_WITHOUT_MEKF = 11, - SAFECTRL_ECLIPSE_DAMPING = 12, - SAFECTRL_ECLIPSE_IDELING = 13, + // OBSW version <= v6.1.0 + LEGACY_SAFECTRL_ACTIVE_MEKF = 10, + LEGACY_SAFECTRL_WITHOUT_MEKF = 11, + LEGACY_SAFECTRL_ECLIPSE_DAMPING = 12, + LEGACY_SAFECTRL_ECLIPSE_IDELING = 13, + // Added in v6.2.0 + SAFECTRL_MEKF = 14, + SAFECTRL_GYR = 15, + SAFECTRL_SUSMGM = 16, + SAFECTRL_ECLIPSE_DAMPING_GYR = 17, + SAFECTRL_ECLIPSE_DAMPING_SUSMGM = 18, + SAFECTRL_ECLIPSE_IDELING = 19, SAFECTRL_DETUMBLE_FULL = 20, SAFECTRL_DETUMBLE_DETERIORATED = 21, }; diff --git a/mission/acs/str/StarTrackerHandler.cpp b/mission/acs/str/StarTrackerHandler.cpp index cf2a5919..a6091d7c 100644 --- a/mission/acs/str/StarTrackerHandler.cpp +++ b/mission/acs/str/StarTrackerHandler.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -18,6 +19,7 @@ extern "C" { #include "OBSWConfig.h" #include "eive/definitions.h" +#include "fsfw/thermal/tcsDefinitions.h" std::atomic_bool JCFG_DONE(false); @@ -87,7 +89,6 @@ void StarTrackerHandler::doStartUp() { default: return; } - solutionSet.setReportingEnabled(true); startupState = StartupState::DONE; internalState = InternalState::IDLE; setMode(_MODE_TO_ON); @@ -106,11 +107,14 @@ void StarTrackerHandler::doShutDown() { solutionSet.caliQx.value = 0.0; solutionSet.caliQy.value = 0.0; solutionSet.caliQz.value = 0.0; - solutionSet.isTrustWorthy = 0; + solutionSet.isTrustWorthy.value = 0; solutionSet.setValidity(false, true); } { PoolReadGuard pg(&temperatureSet); + temperatureSet.fpgaTemperature = thermal::INVALID_TEMPERATURE; + temperatureSet.cmosTemperature = thermal::INVALID_TEMPERATURE; + temperatureSet.mcuTemperature = thermal::INVALID_TEMPERATURE; temperatureSet.setValidity(false, true); } reinitNextSetParam = false; @@ -321,7 +325,7 @@ ReturnValue_t StarTrackerHandler::buildNormalDeviceCommand(DeviceCommandId_t* id ReturnValue_t StarTrackerHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) { switch (internalState) { case InternalState::BOOT_FIRMWARE: { - if (bootState == FwBootState::WAIT_FOR_EXECUTION or bootState == FwBootState::VERIFY_BOOT) { + if (bootState == FwBootState::VERIFY_BOOT or isAwaitingReply()) { return NOTHING_TO_SEND; } if (bootState == FwBootState::NONE) { @@ -345,74 +349,69 @@ ReturnValue_t StarTrackerHandler::buildTransitionDeviceCommand(DeviceCommandId_t *id = startracker::REQ_VERSION; return buildCommandFromCommand(*id, nullptr, 0); } + case (FwBootState::SET_TIME): { + *id = startracker::SET_TIME_FROM_SYS_TIME; + return buildCommandFromCommand(*id, nullptr, 0); + } case (FwBootState::LOGLEVEL): { - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::LOGLEVEL; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); } case (FwBootState::LIMITS): { - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::LIMITS; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); } case (FwBootState::TRACKING): { - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::TRACKING; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); } case FwBootState::MOUNTING: - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::MOUNTING; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); case FwBootState::IMAGE_PROCESSOR: - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::IMAGE_PROCESSOR; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); case FwBootState::CAMERA: - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::CAMERA; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); case FwBootState::CENTROIDING: - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::CENTROIDING; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); case FwBootState::LISA: - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::LISA; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); case FwBootState::MATCHING: - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::MATCHING; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); case FwBootState::VALIDATION: - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::VALIDATION; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); case FwBootState::ALGO: - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::ALGO; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); case FwBootState::LOG_SUBSCRIPTION: - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::LOGSUBSCRIPTION; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); case FwBootState::DEBUG_CAMERA: - bootState = FwBootState::WAIT_FOR_EXECUTION; *id = startracker::DEBUG_CAMERA; return buildCommandFromCommand( *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); + case FwBootState::AUTO_THRESHOLD: + *id = startracker::AUTO_THRESHOLD; + return buildCommandFromCommand( + *id, reinterpret_cast(paramJsonFile.c_str()), paramJsonFile.size()); default: { sif::error << "STR: Unexpected boot state" << (int)bootState << std::endl; return NOTHING_TO_SEND; @@ -455,6 +454,20 @@ ReturnValue_t StarTrackerHandler::buildCommandFromCommand(DeviceCommandId_t devi preparePingRequest(); return returnvalue::OK; } + case (startracker::SET_TIME_FROM_SYS_TIME): { + SetTimeActionRequest setTimeRequest{}; + timeval tv; + Clock::getClock(&tv); + setTimeRequest.unixTime = + (static_cast(tv.tv_sec) * 1000 * 1000) + (static_cast(tv.tv_usec)); + arc_pack_settime_action_req(&setTimeRequest, commandBuffer, &rawPacketLen); + size_t serLen = 0; + // Time in milliseconds. Manual serialization because arcsec API ignores endianness. + SerializeAdapter::serialize(&setTimeRequest.unixTime, commandBuffer + 2, &serLen, + sizeof(commandBuffer) - 2, SerializeIF::Endianness::LITTLE); + rawPacket = commandBuffer; + return returnvalue::OK; + } case (startracker::REQ_TIME): { prepareTimeRequest(); return returnvalue::OK; @@ -547,6 +560,11 @@ ReturnValue_t StarTrackerHandler::buildCommandFromCommand(DeviceCommandId_t devi result = prepareParamCommand(commandData, commandDataLen, jcfgs.logLevel, reinitNextSetParam); return result; } + case (startracker::AUTO_THRESHOLD): { + result = + prepareParamCommand(commandData, commandDataLen, jcfgs.autoThreshold, reinitNextSetParam); + return result; + } case (startracker::LOGSUBSCRIPTION): { result = prepareParamCommand(commandData, commandDataLen, jcfgs.logSubscription, reinitNextSetParam); @@ -651,19 +669,23 @@ void StarTrackerHandler::fillCommandAndReplyMap() { startracker::MAX_FRAME_SIZE * 2 + 2); this->insertInCommandAndReplyMap(startracker::LOGLEVEL, 3, nullptr, startracker::MAX_FRAME_SIZE * 2 + 2); + this->insertInCommandAndReplyMap(startracker::SET_TIME_FROM_SYS_TIME, 2, nullptr, + startracker::MAX_FRAME_SIZE * 2 + 2); this->insertInCommandAndReplyMap(startracker::LOGSUBSCRIPTION, 3, nullptr, startracker::MAX_FRAME_SIZE * 2 + 2); this->insertInCommandAndReplyMap(startracker::DEBUG_CAMERA, 3, nullptr, startracker::MAX_FRAME_SIZE * 2 + 2); this->insertInCommandAndReplyMap(startracker::LIMITS, 3, nullptr, startracker::MAX_FRAME_SIZE * 2 + 2); + this->insertInCommandAndReplyMap(startracker::AUTO_THRESHOLD, 2, nullptr, + startracker::MAX_FRAME_SIZE * 2 + 2); this->insertInCommandAndReplyMap(startracker::MOUNTING, 3, nullptr, startracker::MAX_FRAME_SIZE * 2 + 2); this->insertInCommandAndReplyMap(startracker::IMAGE_PROCESSOR, 3, nullptr, startracker::MAX_FRAME_SIZE * 2 + 2); this->insertInCommandAndReplyMap(startracker::CAMERA, 3, nullptr, startracker::MAX_FRAME_SIZE * 2 + 2); - this->insertInCommandAndReplyMap(startracker::CENTROIDING, 3, nullptr, + this->insertInCommandAndReplyMap(startracker::CENTROIDING, 2, nullptr, startracker::MAX_FRAME_SIZE * 2 + 2); this->insertInCommandAndReplyMap(startracker::LISA, 3, nullptr, startracker::MAX_FRAME_SIZE * 2 + 2); @@ -810,6 +832,7 @@ void StarTrackerHandler::bootFirmware(Mode_t toMode) { setMode(toMode, startracker::SUBMODE_FIRMWARE); } sif::info << "STR: Firmware boot success" << std::endl; + solutionSet.setReportingEnabled(true); internalState = InternalState::IDLE; startupState = StartupState::IDLE; break; @@ -833,6 +856,7 @@ void StarTrackerHandler::setUpJsonCfgs(JsonConfigs& cfgs, const char* paramJsonF cfgs.mounting.init(paramJsonFile); cfgs.limits.init(paramJsonFile); cfgs.subscription.init(paramJsonFile); + cfgs.autoThreshold.init(paramJsonFile); JCFG_DONE = true; } @@ -856,13 +880,20 @@ ReturnValue_t StarTrackerHandler::scanForReply(const uint8_t* start, size_t rema *foundLen = remainingSize; return returnvalue::OK; } - if (remainingSize < 3) { - sif::error << "StarTrackerHandler: Reply packet with length less than 3 is invalid" + if (remainingSize < 2) { + sif::error << "StarTrackerHandler: Reply packet with length " << remainingSize + << " less than " + "2 is invalid" << std::endl; return returnvalue::FAILED; } switch (startracker::getReplyFrameType(start)) { + case TMTC_COMM_ERROR: { + *foundLen = remainingSize; + triggerEvent(COM_ERROR_REPLY_RECEIVED, start[1]); + break; + } case TMTC_ACTIONREPLY: { *foundLen = remainingSize; return scanForActionReply(startracker::getId(start), foundId); @@ -897,6 +928,10 @@ ReturnValue_t StarTrackerHandler::interpretDeviceReply(DeviceCommandId_t id, ReturnValue_t result = returnvalue::OK; switch (id) { + case (startracker::SET_TIME_FROM_SYS_TIME): { + result = handleActionReply(packet); + break; + } case (startracker::REQ_TIME): { result = handleTm(packet, timeSet, startracker::TimeSet::SIZE, "REQ_TIME"); break; @@ -956,7 +991,8 @@ ReturnValue_t StarTrackerHandler::interpretDeviceReply(DeviceCommandId_t id, case (startracker::TRACKING): case (startracker::VALIDATION): case (startracker::IMAGE_PROCESSOR): - case (startracker::ALGO): { + case (startracker::ALGO): + case (startracker::AUTO_THRESHOLD): { result = handleSetParamReply(packet); break; } @@ -1363,6 +1399,10 @@ ReturnValue_t StarTrackerHandler::scanForActionReply(uint8_t replyId, DeviceComm *foundId = startracker::UPLOAD_IMAGE; break; } + case (ARC_ACTION_REQ_SETTIME_ID): { + *foundId = startracker::SET_TIME_FROM_SYS_TIME; + break; + } case (startracker::ID::CHECKSUM): { *foundId = startracker::CHECKSUM; break; @@ -1430,6 +1470,10 @@ ReturnValue_t StarTrackerHandler::scanForSetParameterReply(uint8_t replyId, *foundId = startracker::DEBUG_CAMERA; break; } + case (startracker::ID::AUTO_THRESHOLD): { + *foundId = startracker::AUTO_THRESHOLD; + break; + } case (startracker::ID::LOG_SUBSCRIPTION): { *foundId = startracker::LOGSUBSCRIPTION; break; @@ -1857,7 +1901,7 @@ ReturnValue_t StarTrackerHandler::handleSetParamReply(const uint8_t* rawFrame) { uint8_t status = startracker::getStatusField(rawFrame); if (status != startracker::STATUS_OK) { sif::warning << "StarTrackerHandler::handleSetParamReply: Failed to execute parameter set " - " command with parameter ID" + "command with parameter ID " << static_cast(*(rawFrame + PARAMETER_ID_OFFSET)) << std::endl; if (internalState != InternalState::IDLE) { internalState = InternalState::IDLE; @@ -1865,21 +1909,25 @@ ReturnValue_t StarTrackerHandler::handleSetParamReply(const uint8_t* rawFrame) { return SET_PARAM_FAILED; } if (internalState != InternalState::IDLE) { - handleStartup(*(rawFrame + PARAMETER_ID_OFFSET)); + handleStartup(*rawFrame, *(rawFrame + PARAMETER_ID_OFFSET)); } return returnvalue::OK; } ReturnValue_t StarTrackerHandler::handleActionReply(const uint8_t* rawFrame) { uint8_t status = startracker::getStatusField(rawFrame); + ReturnValue_t result = returnvalue::OK; if (status != startracker::STATUS_OK) { sif::warning << "StarTrackerHandler::handleActionReply: Failed to execute action " << "command with action ID " << static_cast(*(rawFrame + ACTION_ID_OFFSET)) << " and status " << static_cast(status) << std::endl; - return ACTION_FAILED; + result = ACTION_FAILED; } - return returnvalue::OK; + if (internalState != InternalState::IDLE) { + handleStartup(*rawFrame, *(rawFrame + PARAMETER_ID_OFFSET)); + } + return result; } ReturnValue_t StarTrackerHandler::handleChecksumReply(const uint8_t* rawFrame) { @@ -1975,7 +2023,7 @@ ReturnValue_t StarTrackerHandler::checkProgram() { startupState = StartupState::BOOT_BOOTLOADER; } if (bootState == FwBootState::VERIFY_BOOT) { - bootState = FwBootState::LOGLEVEL; + bootState = FwBootState::SET_TIME; } else if (internalState == InternalState::BOOTLOADER_CHECK) { triggerEvent(BOOTING_BOOTLOADER_FAILED_EVENT); internalState = InternalState::FAILED_BOOTLOADER_BOOT; @@ -2052,7 +2100,18 @@ ReturnValue_t StarTrackerHandler::handleActionReplySet(const uint8_t* rawFrame, return result; } -void StarTrackerHandler::handleStartup(uint8_t parameterId) { +void StarTrackerHandler::handleStartup(uint8_t tmType, uint8_t parameterId) { + switch (tmType) { + case (TMTC_ACTIONREPLY): { + case (ARC_ACTION_REQ_SETTIME_ID): { + bootState = FwBootState::LOGLEVEL; + return; + } + default: { + break; + } + } + } switch (parameterId) { case (startracker::ID::LOG_LEVEL): { bootState = FwBootState::LIMITS; @@ -2062,7 +2121,7 @@ void StarTrackerHandler::handleStartup(uint8_t parameterId) { bootState = FwBootState::TRACKING; break; } - case (startracker::ID::TRACKING): { + case (ARC_PARAM_TRACKING_ID): { bootState = FwBootState::MOUNTING; break; } @@ -2103,6 +2162,10 @@ void StarTrackerHandler::handleStartup(uint8_t parameterId) { break; } case (startracker::ID::DEBUG_CAMERA): { + bootState = FwBootState::AUTO_THRESHOLD; + break; + } + case (startracker::ID::AUTO_THRESHOLD): { bootState = FwBootState::NONE; internalState = InternalState::DONE; break; diff --git a/mission/acs/str/StarTrackerHandler.h b/mission/acs/str/StarTrackerHandler.h index 7bcc9f2a..1e790695 100644 --- a/mission/acs/str/StarTrackerHandler.h +++ b/mission/acs/str/StarTrackerHandler.h @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -16,7 +17,7 @@ #include "fsfw/timemanager/Countdown.h" extern "C" { -#include +#include } /** @@ -143,6 +144,8 @@ class StarTrackerHandler : public DeviceHandlerBase { static const Event BOOTING_FIRMWARE_FAILED_EVENT = MAKE_EVENT(1, severity::LOW); //! [EXPORT] : [COMMENT] Failed to boot star tracker into bootloader mode static const Event BOOTING_BOOTLOADER_FAILED_EVENT = MAKE_EVENT(2, severity::LOW); + //! [EXPORT] : [COMMENT] Received COM error. P1: Communication Error ID (datasheet p32) + static constexpr Event COM_ERROR_REPLY_RECEIVED = MAKE_EVENT(3, severity::LOW); static const uint8_t STATUS_OFFSET = 2; static const uint8_t PARAMS_OFFSET = 2; @@ -226,6 +229,7 @@ class StarTrackerHandler : public DeviceHandlerBase { Mounting mounting; Limits limits; Subscription subscription; + AutoThreshold autoThreshold; }; JsonConfigs jcfgs; Countdown jcfgCountdown = Countdown(250); @@ -264,6 +268,7 @@ class StarTrackerHandler : public DeviceHandlerBase { BOOT_DELAY, REQ_VERSION, VERIFY_BOOT, + SET_TIME, LOGLEVEL, LIMITS, TRACKING, @@ -278,7 +283,9 @@ class StarTrackerHandler : public DeviceHandlerBase { ALGO, LOG_SUBSCRIPTION, DEBUG_CAMERA, + AUTO_THRESHOLD, WAIT_FOR_EXECUTION, + RETRY_CFG_CMD }; FwBootState bootState = FwBootState::NONE; @@ -468,7 +475,7 @@ class StarTrackerHandler : public DeviceHandlerBase { /** * @brief Handles the startup state machine */ - void handleStartup(uint8_t parameterId); + void handleStartup(uint8_t tmType, uint8_t parameterId); /** * @brief Handles telemtry replies and fills the appropriate dataset diff --git a/mission/acs/str/arcsecJsonKeys.h b/mission/acs/str/arcsecJsonKeys.h index f9e2f4f1..c44ece9f 100644 --- a/mission/acs/str/arcsecJsonKeys.h +++ b/mission/acs/str/arcsecJsonKeys.h @@ -176,6 +176,13 @@ static const char DEBUG_CAMERA[] = "DebugCamera"; static const char TIMING[] = "timing"; static const char TEST[] = "test"; +static constexpr char AUTO_THRESHOLD[] = "AutoThreshold"; +static constexpr char AT_MODE[] = "mode"; +static constexpr char AT_DESIRED_BLOB_COUNTS[] = "desiredBlobsCount"; +static constexpr char AT_MIN_THRESHOLD[] = "minThreshold"; +static constexpr char AT_MAX_THRESHOLD[] = "maxThreshold"; +static constexpr char AT_THRESHOLD_KP[] = "thresholdKp"; + } // namespace arcseckeys #endif /* BSP_Q7S_DEVICES_DEVICEDEFINITIONS_ARCSECJSONKEYS_H_ */ diff --git a/mission/acs/str/strHelpers.h b/mission/acs/str/strHelpers.h index 69729df5..c6336186 100644 --- a/mission/acs/str/strHelpers.h +++ b/mission/acs/str/strHelpers.h @@ -326,6 +326,8 @@ static const DeviceCommandId_t DEBUG_CAMERA = 83; static const DeviceCommandId_t FIRMWARE_UPDATE = 84; static const DeviceCommandId_t DISABLE_TIMESTAMP_GENERATION = 85; static const DeviceCommandId_t ENABLE_TIMESTAMP_GENERATION = 86; +static constexpr DeviceCommandId_t SET_TIME_FROM_SYS_TIME = 87; +static constexpr DeviceCommandId_t AUTO_THRESHOLD = 88; static const DeviceCommandId_t NONE = 0xFFFFFFFF; static const uint32_t VERSION_SET_ID = REQ_VERSION; @@ -396,7 +398,6 @@ static const uint8_t ALGO = 16; static const uint8_t REBOOT = 7; static const uint8_t UPLOAD_IMAGE = 10; static const uint8_t POWER = 11; -static const uint8_t SET_TIME = 14; static const uint8_t SUBSCRIPTION = 18; static const uint8_t SOLUTION = 24; static const uint8_t TEMPERATURE = 27; @@ -410,6 +411,7 @@ static const uint8_t TAKE_IMAGE = 15; static const uint8_t LOG_LEVEL = 3; static const uint8_t LOG_SUBSCRIPTION = 19; static const uint8_t DEBUG_CAMERA = 20; +static const uint8_t AUTO_THRESHOLD = 23; } // namespace ID namespace Program { @@ -441,6 +443,21 @@ static const uint32_t secret[16]{ REGION_12_SECRET, REGION_13_SECRET, REGION_14_SECRET, REGION_15_SECRET}; } // namespace region_secrets +namespace comError { +enum Id { + BAD_CRC = 1, + UNKNOWN_TM_ID = 2, + UNKNOWN_PARAM_ID = 3, + UNKNOWN_ACTION_REQ = 4, + INVALID_TM_SIZE = 5, + INVALID_PARAM_SIZE = 6, + INVALID_ACTION_REQ_SIZE = 7, + FRAME_TOO_SHORT = 8, + INVALID_FRAME_TYPE = 9, + UNKNOWN_ERROR = 10 +}; +} + enum class FlashSections : uint8_t { BOOTLOADER_SECTION = 0, MAIN_FIRMWARE_SECTION = 1, @@ -650,7 +667,7 @@ class SolutionSet : public StaticLocalDataSet { // Ticks timestamp lp_var_t ticks = lp_var_t(sid.objectId, PoolIds::TICKS_SOLUTION_SET, this); /// Unix time stamp - lp_var_t time = lp_var_t(sid.objectId, PoolIds::TIME_SOLUTION_SET, this); + lp_var_t timeUs = lp_var_t(sid.objectId, PoolIds::TIME_SOLUTION_SET, this); // Calibrated quaternion (takes into account the mounting quaternion), typically same as // track q values lp_var_t caliQw = lp_var_t(sid.objectId, PoolIds::CALI_QW, this); @@ -695,7 +712,7 @@ class SolutionSet : public StaticLocalDataSet { void printSet() { PoolReadGuard rg(this); sif::info << "SolutionSet::printSet: Ticks: " << this->ticks << std::endl; - sif::info << "SolutionSet::printSet: Time: " << this->time << std::endl; + sif::info << "SolutionSet::printSet: Time: " << this->timeUs << std::endl; sif::info << "SolutionSet::printSet: Calibrated quaternion Qw: " << this->caliQw << std::endl; sif::info << "SolutionSet::printSet: Calibrated quaternion Qx: " << this->caliQx << std::endl; sif::info << "SolutionSet::printSet: Calibrated quaternion Qy: " << this->caliQy << std::endl; diff --git a/mission/acs/str/strJsonCommands.cpp b/mission/acs/str/strJsonCommands.cpp index 134a00a0..5a910f87 100644 --- a/mission/acs/str/strJsonCommands.cpp +++ b/mission/acs/str/strJsonCommands.cpp @@ -916,3 +916,45 @@ ReturnValue_t DebugCamera::createCommand(uint8_t* buffer) { adduint32(param, buffer + offset); return returnvalue::OK; } + +AutoThreshold::AutoThreshold() : ArcsecJsonParamBase(arcseckeys::AUTO_THRESHOLD) {} + +size_t AutoThreshold::getSize() { return COMMAND_SIZE; } + +ReturnValue_t AutoThreshold::createCommand(uint8_t* buffer) { + ReturnValue_t result = returnvalue::OK; + uint8_t offset = 0; + std::string param; + addSetParamHeader(buffer, startracker::ID::AUTO_THRESHOLD); + offset = 2; + result = getParam(arcseckeys::AT_MODE, param); + if (result != returnvalue::OK) { + return result; + } + adduint8(param, buffer + offset); + offset += 1; + result = getParam(arcseckeys::AT_DESIRED_BLOB_COUNTS, param); + if (result != returnvalue::OK) { + return result; + } + adduint8(param, buffer + offset); + offset += 1; + result = getParam(arcseckeys::AT_MIN_THRESHOLD, param); + if (result != returnvalue::OK) { + return result; + } + adduint16(param, buffer + offset); + offset += 2; + result = getParam(arcseckeys::AT_MAX_THRESHOLD, param); + if (result != returnvalue::OK) { + return result; + } + adduint16(param, buffer + offset); + offset += 2; + result = getParam(arcseckeys::AT_THRESHOLD_KP, param); + if (result != returnvalue::OK) { + return result; + } + addfloat(param, buffer + offset); + return returnvalue::OK; +} diff --git a/mission/acs/str/strJsonCommands.h b/mission/acs/str/strJsonCommands.h index 99588f9c..7e75c0b1 100644 --- a/mission/acs/str/strJsonCommands.h +++ b/mission/acs/str/strJsonCommands.h @@ -222,6 +222,22 @@ class LogSubscription : public ArcsecJsonParamBase { ReturnValue_t createCommand(uint8_t* buffer) override; }; +/** + * @brief Generates command to set log subscription parameters. + * + */ +class AutoThreshold : public ArcsecJsonParamBase { + public: + AutoThreshold(); + + size_t getSize(); + + private: + static const size_t COMMAND_SIZE = 12; + + ReturnValue_t createCommand(uint8_t* buffer) override; +}; + /** * @brief Generates command to set debug camera parameters * diff --git a/mission/cfdp/CfdpHandler.cpp b/mission/cfdp/CfdpHandler.cpp index baf501e4..fa35535c 100644 --- a/mission/cfdp/CfdpHandler.cpp +++ b/mission/cfdp/CfdpHandler.cpp @@ -93,7 +93,8 @@ ReturnValue_t CfdpHandler::handleCfdpPacket(TmTcMessage& msg) { return INVALID_PDU_FORMAT; } if (not FileDirectiveReader::checkFileDirective(pduDataField[0])) { - sif::error << "CfdpHandler: Invalid PDU directive field " << pduDataField[0] << std::endl; + sif::error << "CfdpHandler: Invalid PDU directive field " << static_cast(pduDataField[0]) + << std::endl; return INVALID_DIRECTIVE_FIELD; } auto directive = static_cast(pduDataField[0]); diff --git a/mission/com/CcsdsIpCoreHandler.cpp b/mission/com/CcsdsIpCoreHandler.cpp index 625c90cd..19dd4f5a 100644 --- a/mission/com/CcsdsIpCoreHandler.cpp +++ b/mission/com/CcsdsIpCoreHandler.cpp @@ -246,7 +246,13 @@ ReturnValue_t CcsdsIpCoreHandler::checkModeCommand(Mode_t mode, Submode_t submod void CcsdsIpCoreHandler::startTransition(Mode_t mode, Submode_t submode) { triggerEvent(CHANGING_MODE, mode, submode); if (mode == HasModesIF::MODE_ON) { - if (this->submode != submode) { + uint32_t currentRate = ptmeConfig.getRate(); + // Check whether the rate actually changes. + if ((this->submode != submode) and + (((submode == static_cast(com::CcsdsSubmode::DATARATE_LOW) and + (currentRate != RATE_100KBPS))) or + ((submode == static_cast(com::CcsdsSubmode::DATARATE_HIGH) and + (currentRate != RATE_500KBPS))))) { initPtmeUpdateAfterXCycles(); updateContext.enableTransmitAfterPtmeUpdate = true; updateContext.updateClockRate = true; diff --git a/mission/com/LiveTmTask.cpp b/mission/com/LiveTmTask.cpp index d09c6ced..39648c15 100644 --- a/mission/com/LiveTmTask.cpp +++ b/mission/com/LiveTmTask.cpp @@ -19,13 +19,8 @@ LiveTmTask::LiveTmTask(object_id_t objectId, PusTmFunnel& pusFunnel, CfdpTmFunne ReturnValue_t LiveTmTask::performOperation(uint8_t opCode) { readCommandQueue(); while (true) { - bool performWriteOp = true; - if (mode == MODE_OFF or ptmeLocked) { - performWriteOp = false; - } - // The funnel tasks are scheduled here directly as well. - ReturnValue_t result = channel.handleNextTm(performWriteOp); + ReturnValue_t result = channel.handleNextTm(!ptmeLocked); if (result == DirectTmSinkIF::IS_BUSY) { sif::error << "Lost live TM, PAPB busy" << std::endl; } diff --git a/mission/com/PersistentLogTmStoreTask.cpp b/mission/com/PersistentLogTmStoreTask.cpp index 77f2bb7d..28545457 100644 --- a/mission/com/PersistentLogTmStoreTask.cpp +++ b/mission/com/PersistentLogTmStoreTask.cpp @@ -42,13 +42,7 @@ ReturnValue_t PersistentLogTmStoreTask::performOperation(uint8_t opCode) { if (not someonesBusy) { TaskFactory::delayTask(100); } else if (vcBusyDuringDump) { - // TODO: Might not be necessary - sif::debug << "VC busy, delaying" << std::endl; TaskFactory::delayTask(10); - } else { - // TODO: Would be best to remove this, but not delaying here can lead to evil issues. - // Polling the PAPB of the PTME core too often leads to scheuduling issues. - TaskFactory::delayTask(2); } } } diff --git a/mission/com/PersistentSingleTmStoreTask.cpp b/mission/com/PersistentSingleTmStoreTask.cpp index 1b77365b..d6f43289 100644 --- a/mission/com/PersistentSingleTmStoreTask.cpp +++ b/mission/com/PersistentSingleTmStoreTask.cpp @@ -24,13 +24,7 @@ ReturnValue_t PersistentSingleTmStoreTask::performOperation(uint8_t opCode) { if (not busy) { TaskFactory::delayTask(100); } else if (dumpContext.vcBusyDuringDump) { - sif::debug << "VC busy, delaying" << std::endl; - // TODO: Might not be necessary TaskFactory::delayTask(10); - } else { - // TODO: Would be best to remove this, but not delaying here can lead to evil issues. - // Polling the PAPB of the PTME core too often leads to scheuduling issues. - TaskFactory::delayTask(2); } } } diff --git a/mission/com/SyrlinksHandler.cpp b/mission/com/SyrlinksHandler.cpp index 6fbc8dc2..50e6a56e 100644 --- a/mission/com/SyrlinksHandler.cpp +++ b/mission/com/SyrlinksHandler.cpp @@ -773,11 +773,13 @@ void SyrlinksHandler::doTransition(Mode_t modeFrom, Submode_t subModeFrom) { auto txStandbyHandler = [&]() { txDataset.setReportingEnabled(false); poolManager.changeCollectionInterval(temperatureSet.getSid(), 60.0); + poolManager.changeCollectionInterval(rxDataset.getSid(), 60.0); transState = TransitionState::SET_TX_STANDBY; internalState = InternalState::TX_TRANSITION; }; auto txOnHandler = [&](TransitionState tgtTransitionState) { txDataset.setReportingEnabled(true); + poolManager.changeCollectionInterval(rxDataset.getSid(), 5.0); poolManager.changeCollectionInterval(txDataset.getSid(), 10.0); poolManager.changeCollectionInterval(temperatureSet.getSid(), 5.0); transState = tgtTransitionState; diff --git a/mission/com/TmStoreTaskBase.cpp b/mission/com/TmStoreTaskBase.cpp index 0470dc04..80900975 100644 --- a/mission/com/TmStoreTaskBase.cpp +++ b/mission/com/TmStoreTaskBase.cpp @@ -45,13 +45,19 @@ bool TmStoreTaskBase::handleOneStore(PersistentTmStoreWithTmQueue& store, } else { Command_t execCmd; // Handle TC requests, for example deletion or retrieval requests. + // TODO: Not really clean here.. would be better if the executed command is returns as an + // enumeration. result = store.handleCommandQueue(ipcStore, execCmd); - if (result == returnvalue::OK) { - if (execCmd == TmStoreMessage::DOWNLINK_STORE_CONTENT_TIME) { + if (execCmd == TmStoreMessage::DOWNLINK_STORE_CONTENT_TIME) { + if (result == PersistentTmStore::DUMP_DONE) { + dumpDoneHandler(store, dumpContext); + } else if (result == returnvalue::OK) { cancelDumpCd.resetTimer(); tmSinkBusyCd.resetTimer(); dumpContext.reset(); } + } + if (execCmd != CommandMessageIF::CMD_NONE) { tcRequestReceived = true; } } @@ -119,21 +125,13 @@ ReturnValue_t TmStoreTaskBase::performDump(PersistentTmStoreWithTmQueue& store, DumpContext& dumpContext, bool& dumpPerformed) { size_t dumpedLen = 0; - auto dumpDoneHandler = [&]() { - uint32_t startTime; - uint32_t endTime; - store.getStartAndEndTimeCurrentOrLastDump(startTime, endTime); - triggerEvent(dumpContext.eventIfDone, dumpContext.numberOfDumpedPackets, - dumpContext.dumpedBytes); - dumpContext.reset(); - }; // Dump the next packet into the PTME. dumpContext.ptmeBusyCounter = 0; tmSinkBusyCd.resetTimer(); ReturnValue_t result = store.getNextDumpPacket(tmReader, fileHasSwapped); if (fileHasSwapped and result == PersistentTmStore::DUMP_DONE) { // This can happen if a file is corrupted and the next file swap completes the dump. - dumpDoneHandler(); + dumpDoneHandler(store, dumpContext); return returnvalue::OK; } else if (result != returnvalue::OK) { sif::error << "PersistentTmStore: Getting next dump packet failed" << std::endl; @@ -157,7 +155,7 @@ ReturnValue_t TmStoreTaskBase::performDump(PersistentTmStoreWithTmQueue& store, } } if (result == PersistentTmStore::DUMP_DONE) { - dumpDoneHandler(); + dumpDoneHandler(store, dumpContext); } return returnvalue::OK; } @@ -198,6 +196,14 @@ ReturnValue_t TmStoreTaskBase::connectModeTreeParent(HasModeTreeChildrenIF& pare return modetree::connectModeTreeParent(parent, *this, nullptr, modeHelper); } +void TmStoreTaskBase::dumpDoneHandler(PersistentTmStore& store, DumpContext& dumpContext) { + uint32_t startTime; + uint32_t endTime; + store.getStartAndEndTimeCurrentOrLastDump(startTime, endTime); + triggerEvent(dumpContext.eventIfDone, dumpContext.numberOfDumpedPackets, dumpContext.dumpedBytes); + dumpContext.reset(); +} + ModeTreeChildIF& TmStoreTaskBase::getModeTreeChildIF() { return *this; } void TmStoreTaskBase::readCommandQueue(void) { diff --git a/mission/com/TmStoreTaskBase.h b/mission/com/TmStoreTaskBase.h index ef61bd19..2bcd3b1e 100644 --- a/mission/com/TmStoreTaskBase.h +++ b/mission/com/TmStoreTaskBase.h @@ -96,6 +96,8 @@ class TmStoreTaskBase : public SystemObject, ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, uint32_t* msToReachTheMode) override; + void dumpDoneHandler(PersistentTmStore& store, DumpContext& dumpContext); + void announceMode(bool recursive) override; object_id_t getObjectId() const override; const HasHealthIF* getOptHealthIF() const override; diff --git a/mission/controller/AcsController.cpp b/mission/controller/AcsController.cpp index 04009cb2..08e8760d 100644 --- a/mission/controller/AcsController.cpp +++ b/mission/controller/AcsController.cpp @@ -7,6 +7,7 @@ AcsController::AcsController(object_id_t objectId, bool enableHkSets) : ExtendedControllerBase(objectId), enableHkSets(enableHkSets), + fusedRotationEstimation(&acsParameters), guidance(&acsParameters), safeCtrl(&acsParameters), ptgCtrl(&acsParameters), @@ -20,7 +21,8 @@ AcsController::AcsController(object_id_t objectId, bool enableHkSets) gpsDataProcessed(this), mekfData(this), ctrlValData(this), - actuatorCmdData(this) {} + actuatorCmdData(this), + fusedRotRateData(this) {} ReturnValue_t AcsController::initialize() { ReturnValue_t result = parameterHelper.initialize(); @@ -146,6 +148,8 @@ void AcsController::performSafe() { sensorProcessing.process(now, &sensorValues, &mgmDataProcessed, &susDataProcessed, &gyrDataProcessed, &gpsDataProcessed, &acsParameters); + fusedRotationEstimation.estimateFusedRotationRateSafe(&susDataProcessed, &mgmDataProcessed, + &gyrDataProcessed, &fusedRotRateData); ReturnValue_t result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed, &susDataProcessed, &mekfData, &acsParameters); if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && @@ -172,25 +176,42 @@ void AcsController::performSafe() { acs::SafeModeStrategy safeCtrlStrat = safeCtrl.safeCtrlStrategy( mgmDataProcessed.mgmVecTot.isValid(), not mekfInvalidFlag, gyrDataProcessed.gyrVecTot.isValid(), susDataProcessed.susVecTot.isValid(), + fusedRotRateData.rotRateOrthogonal.isValid(), fusedRotRateData.rotRateTotal.isValid(), acsParameters.safeModeControllerParameters.useMekf, + acsParameters.safeModeControllerParameters.useGyr, acsParameters.safeModeControllerParameters.dampingDuringEclipse); switch (safeCtrlStrat) { - case (acs::SafeModeStrategy::SAFECTRL_ACTIVE_MEKF): + case (acs::SafeModeStrategy::SAFECTRL_MEKF): safeCtrl.safeMekf(mgmDataProcessed.mgmVecTot.value, mekfData.satRotRateMekf.value, susDataProcessed.sunIjkModel.value, mekfData.quatMekf.value, sunTargetDir, magMomMtq, errAng); safeCtrlFailureFlag = false; safeCtrlFailureCounter = 0; break; - case (acs::SafeModeStrategy::SAFECTRL_WITHOUT_MEKF): - safeCtrl.safeNonMekf(mgmDataProcessed.mgmVecTot.value, gyrDataProcessed.gyrVecTot.value, - susDataProcessed.susVecTot.value, sunTargetDir, magMomMtq, errAng); + case (acs::SafeModeStrategy::SAFECTRL_GYR): + safeCtrl.safeGyr(mgmDataProcessed.mgmVecTot.value, gyrDataProcessed.gyrVecTot.value, + susDataProcessed.susVecTot.value, sunTargetDir, magMomMtq, errAng); safeCtrlFailureFlag = false; safeCtrlFailureCounter = 0; break; - case (acs::SafeModeStrategy::SAFECTRL_ECLIPSE_DAMPING): - safeCtrl.safeRateDamping(mgmDataProcessed.mgmVecTot.value, gyrDataProcessed.gyrVecTot.value, - sunTargetDir, magMomMtq, errAng); + case (acs::SafeModeStrategy::SAFECTRL_SUSMGM): + safeCtrl.safeSusMgm(mgmDataProcessed.mgmVecTot.value, fusedRotRateData.rotRateParallel.value, + fusedRotRateData.rotRateOrthogonal.value, + susDataProcessed.susVecTot.value, sunTargetDir, magMomMtq, errAng); + safeCtrlFailureFlag = false; + safeCtrlFailureCounter = 0; + break; + case (acs::SafeModeStrategy::SAFECTRL_ECLIPSE_DAMPING_GYR): + safeCtrl.safeRateDampingGyr(mgmDataProcessed.mgmVecTot.value, + gyrDataProcessed.gyrVecTot.value, sunTargetDir, magMomMtq, + errAng); + safeCtrlFailureFlag = false; + safeCtrlFailureCounter = 0; + break; + case (acs::SafeModeStrategy::SAFECTRL_ECLIPSE_DAMPING_SUSMGM): + safeCtrl.safeRateDampingSusMgm(mgmDataProcessed.mgmVecTot.value, + fusedRotRateData.rotRateTotal.value, sunTargetDir, magMomMtq, + errAng); safeCtrlFailureFlag = false; safeCtrlFailureCounter = 0; break; @@ -214,12 +235,20 @@ void AcsController::performSafe() { acsParameters.magnetorquerParameter.dipoleMax, magMomMtq, cmdDipoleMtqs); // detumble check and switch - if (mekfData.satRotRateMekf.isValid() && acsParameters.safeModeControllerParameters.useMekf && - VectorOperations::norm(mekfData.satRotRateMekf.value, 3) > - acsParameters.detumbleParameter.omegaDetumbleStart) { - detumbleCounter++; - } else if (gyrDataProcessed.gyrVecTot.isValid() && - VectorOperations::norm(gyrDataProcessed.gyrVecTot.value, 3) > + if (acsParameters.safeModeControllerParameters.useMekf) { + if (mekfData.satRotRateMekf.isValid() and + VectorOperations::norm(mekfData.satRotRateMekf.value, 3) > + acsParameters.detumbleParameter.omegaDetumbleStart) { + detumbleCounter++; + } + } else if (acsParameters.safeModeControllerParameters.useGyr) { + if (gyrDataProcessed.gyrVecTot.isValid() and + VectorOperations::norm(gyrDataProcessed.gyrVecTot.value, 3) > + acsParameters.detumbleParameter.omegaDetumbleStart) { + detumbleCounter++; + } + } else if (fusedRotRateData.rotRateTotal.isValid() and + VectorOperations::norm(fusedRotRateData.rotRateTotal.value, 3) > acsParameters.detumbleParameter.omegaDetumbleStart) { detumbleCounter++; } else if (detumbleCounter > 0) { @@ -244,6 +273,8 @@ void AcsController::performDetumble() { sensorProcessing.process(now, &sensorValues, &mgmDataProcessed, &susDataProcessed, &gyrDataProcessed, &gpsDataProcessed, &acsParameters); + fusedRotationEstimation.estimateFusedRotationRateSafe(&susDataProcessed, &mgmDataProcessed, + &gyrDataProcessed, &fusedRotRateData); ReturnValue_t result = navigation.useMekf(&sensorValues, &gyrDataProcessed, &mgmDataProcessed, &susDataProcessed, &mekfData, &acsParameters); if (result != MultiplicativeKalmanFilter::MEKF_RUNNING && @@ -289,17 +320,26 @@ void AcsController::performDetumble() { actuatorCmd.cmdDipoleMtq(*acsParameters.magnetorquerParameter.inverseAlignment, acsParameters.magnetorquerParameter.dipoleMax, magMomMtq, cmdDipoleMtqs); - if (mekfData.satRotRateMekf.isValid() && - VectorOperations::norm(mekfData.satRotRateMekf.value, 3) < - acsParameters.detumbleParameter.omegaDetumbleEnd) { - detumbleCounter++; - } else if (gyrDataProcessed.gyrVecTot.isValid() && - VectorOperations::norm(gyrDataProcessed.gyrVecTot.value, 3) < + if (acsParameters.safeModeControllerParameters.useMekf) { + if (mekfData.satRotRateMekf.isValid() and + VectorOperations::norm(mekfData.satRotRateMekf.value, 3) < + acsParameters.detumbleParameter.omegaDetumbleEnd) { + detumbleCounter++; + } + } else if (acsParameters.safeModeControllerParameters.useGyr) { + if (gyrDataProcessed.gyrVecTot.isValid() and + VectorOperations::norm(gyrDataProcessed.gyrVecTot.value, 3) < + acsParameters.detumbleParameter.omegaDetumbleEnd) { + detumbleCounter++; + } + } else if (fusedRotRateData.rotRateTotal.isValid() and + VectorOperations::norm(fusedRotRateData.rotRateTotal.value, 3) < acsParameters.detumbleParameter.omegaDetumbleEnd) { detumbleCounter++; } else if (detumbleCounter > 0) { detumbleCounter -= 1; } + if (detumbleCounter > acsParameters.detumbleParameter.detumblecounter) { detumbleCounter = 0; // Triggers safe mode transition in subsystem @@ -307,7 +347,7 @@ void AcsController::performDetumble() { startTransition(mode, acs::SafeSubmode::DEFAULT); } - disableCtrlValData(); + updateCtrlValData(safeCtrlStrat); updateActuatorCmdData(cmdDipoleMtqs); commandActuators(cmdDipoleMtqs[0], cmdDipoleMtqs[1], cmdDipoleMtqs[2], acsParameters.magnetorquerParameter.torqueDuration); @@ -578,6 +618,23 @@ void AcsController::updateActuatorCmdData(const double *rwTargetTorque, } } +void AcsController::updateCtrlValData(uint8_t safeModeStrat) { + PoolReadGuard pg(&ctrlValData); + if (pg.getReadResult() == returnvalue::OK) { + std::memcpy(ctrlValData.tgtQuat.value, ZERO_VEC4, 4 * sizeof(double)); + ctrlValData.tgtQuat.setValid(false); + std::memcpy(ctrlValData.errQuat.value, ZERO_VEC4, 4 * sizeof(double)); + ctrlValData.errQuat.setValid(false); + ctrlValData.errAng.value = 0; + ctrlValData.errAng.setValid(false); + std::memcpy(ctrlValData.tgtRotRate.value, ZERO_VEC3, 3 * sizeof(double)); + ctrlValData.tgtRotRate.setValid(false); + ctrlValData.safeStrat.value = safeModeStrat; + ctrlValData.safeStrat.setValid(true); + ctrlValData.setValidity(true, false); + } +} + void AcsController::updateCtrlValData(double errAng, uint8_t safeModeStrat) { PoolReadGuard pg(&ctrlValData); if (pg.getReadResult() == returnvalue::OK) { @@ -608,17 +665,6 @@ void AcsController::updateCtrlValData(const double *tgtQuat, const double *errQu } } -void AcsController::disableCtrlValData() { - PoolReadGuard pg(&ctrlValData); - if (pg.getReadResult() == returnvalue::OK) { - std::memcpy(ctrlValData.tgtQuat.value, ZERO_VEC4, 4 * sizeof(double)); - std::memcpy(ctrlValData.errQuat.value, ZERO_VEC4, 4 * sizeof(double)); - ctrlValData.errAng.value = 0; - std::memcpy(ctrlValData.tgtRotRate.value, ZERO_VEC3, 3 * sizeof(double)); - ctrlValData.setValidity(false, true); - } -} - ReturnValue_t AcsController::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { // MGM Raw @@ -707,6 +753,11 @@ ReturnValue_t AcsController::initializeLocalDataPool(localpool::DataPool &localD localDataPoolMap.emplace(acsctrl::PoolIds::RW_TARGET_SPEED, &rwTargetSpeed); localDataPoolMap.emplace(acsctrl::PoolIds::MTQ_TARGET_DIPOLE, &mtqTargetDipole); poolManager.subscribeForRegularPeriodicPacket({actuatorCmdData.getSid(), enableHkSets, 10.0}); + // Fused Rot Rate + localDataPoolMap.emplace(acsctrl::PoolIds::ROT_RATE_ORTHOGONAL, &rotRateOrthogonal); + localDataPoolMap.emplace(acsctrl::PoolIds::ROT_RATE_PARALLEL, &rotRateParallel); + localDataPoolMap.emplace(acsctrl::PoolIds::ROT_RATE_TOTAL, &rotRateTotal); + poolManager.subscribeForRegularPeriodicPacket({fusedRotRateData.getSid(), enableHkSets, 10.0}); return returnvalue::OK; } @@ -732,6 +783,8 @@ LocalPoolDataSetBase *AcsController::getDataSetHandle(sid_t sid) { return &ctrlValData; case acsctrl::ACTUATOR_CMD_DATA: return &actuatorCmdData; + case acsctrl::FUSED_ROTATION_RATE_DATA: + return &fusedRotRateData; default: return nullptr; } diff --git a/mission/controller/AcsController.h b/mission/controller/AcsController.h index 0c8b94bb..a7dfbf6a 100644 --- a/mission/controller/AcsController.h +++ b/mission/controller/AcsController.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ class AcsController : public ExtendedControllerBase, public ReceivesParameterMes AcsParameters acsParameters; SensorProcessing sensorProcessing; + FusedRotationEstimation fusedRotationEstimation; Navigation navigation; ActuatorCmd actuatorCmd; Guidance guidance; @@ -115,10 +117,10 @@ class AcsController : public ExtendedControllerBase, public ReceivesParameterMes void updateActuatorCmdData(const int16_t* mtqTargetDipole); void updateActuatorCmdData(const double* rwTargetTorque, const int32_t* rwTargetSpeed, const int16_t* mtqTargetDipole); + void updateCtrlValData(uint8_t safeModeStrat); void updateCtrlValData(double errAng, uint8_t safeModeStrat); void updateCtrlValData(const double* tgtQuat, const double* errQuat, double errAng, const double* tgtRotRate); - void disableCtrlValData(); /* ACS Sensor Values */ ACS::SensorValues sensorValues; @@ -226,6 +228,12 @@ class AcsController : public ExtendedControllerBase, public ReceivesParameterMes PoolEntry rwTargetSpeed = PoolEntry(4); PoolEntry mtqTargetDipole = PoolEntry(3); + // Fused Rot Rate + acsctrl::FusedRotRateData fusedRotRateData; + PoolEntry rotRateOrthogonal = PoolEntry(3); + PoolEntry rotRateParallel = PoolEntry(3); + PoolEntry rotRateTotal = PoolEntry(3); + // Initial delay to make sure all pool variables have been initialized their owners Countdown initialCountdown = Countdown(INIT_DELAY); }; diff --git a/mission/controller/ThermalController.cpp b/mission/controller/ThermalController.cpp index 53babbd5..5f2277a9 100644 --- a/mission/controller/ThermalController.cpp +++ b/mission/controller/ThermalController.cpp @@ -1,6 +1,6 @@ #include "ThermalController.h" -#include +#include #include #include #include @@ -24,13 +24,16 @@ #define LOWER_RW_UPPER_LIMITS 0 ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater, - const std::atomic_bool& tcsBoardShortUnavailable) + const std::atomic_bool& tcsBoardShortUnavailable, + bool pollPcdu1Tmp) : ExtendedControllerBase(objectId), heaterHandler(heater), + pollPcdu1Tmp(pollPcdu1Tmp), sensorTemperatures(this), susTemperatures(this), deviceTemperatures(this), heaterInfo(this), + tcsCtrlInfo(this), imtqThermalSet(objects::IMTQ_HANDLER, ThermalStateCfg()), maxSet0PlocHspd(objects::RTD_0_IC3_PLOC_HEATSPREADER, EiveMax31855::RtdCommands::EXCHANGE_SET_ID), @@ -55,8 +58,6 @@ ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater tmp1075SetTcs0(objects::TMP1075_HANDLER_TCS_0), tmp1075SetTcs1(objects::TMP1075_HANDLER_TCS_1), tmp1075SetPlPcdu0(objects::TMP1075_HANDLER_PLPCDU_0), - // damaged - // tmp1075SetPlPcdu1(objects::TMP1075_HANDLER_PLPCDU_1), tmp1075SetIfBoard(objects::TMP1075_HANDLER_IF_BOARD), susSet0(objects::SUS_0_N_LOC_XFYFZM_PT_XF), susSet1(objects::SUS_1_N_LOC_XBYFZM_PT_XB), @@ -71,6 +72,9 @@ ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater susSet10(objects::SUS_10_N_LOC_XMYBZF_PT_ZF), susSet11(objects::SUS_11_R_LOC_XBYMZB_PT_ZB), tcsBrdShortlyUnavailable(tcsBoardShortUnavailable) { + if (pollPcdu1Tmp) { + tmp1075SetPlPcdu1 = new TMP1075::Tmp1075Dataset(objects::TMP1075_HANDLER_PLPCDU_1); + } resetSensorsArray(); } @@ -171,7 +175,7 @@ void ThermalController::performControlOperation() { } } - HeaterSwitchStates heaterSwitchStateArray{}; + tcsCtrl::HeaterSwitchStates heaterSwitchStateArray{}; heaterHandler.getAllSwitchStates(heaterSwitchStateArray); { PoolReadGuard pg(&heaterInfo); @@ -188,12 +192,11 @@ void ThermalController::performControlOperation() { if (transitionWhenHeatersOff) { bool allSwitchersOff = true; for (size_t idx = 0; idx < heaterSwitchStateArray.size(); idx++) { - if (heaterSwitchStateArray[idx] != HeaterHandler::SwitchState::OFF) { + if (heaterSwitchStateArray[idx] != heater::SwitchState::OFF) { allSwitchersOff = false; // if heater still ON after 3 cycles, switch OFF again if (transitionWhenHeatersOffCycles == 3) { - heaterHandler.switchHeater(static_cast(idx), - HeaterHandler::SwitchState::OFF); + heaterHandler.switchHeater(static_cast(idx), heater::SwitchState::OFF); triggerEvent(tcsCtrl::HEATER_NOT_OFF_FOR_OFF_MODE); } } @@ -206,8 +209,21 @@ void ThermalController::performControlOperation() { } else { transitionWhenHeatersOffCycles++; } - } else if (mode != MODE_OFF and not tcsBrdShortlyUnavailable) { - performThermalModuleCtrl(heaterSwitchStateArray); + } else if (mode != MODE_OFF) { + if (not tcsBrdShortlyUnavailable) { + performThermalModuleCtrl(heaterSwitchStateArray); + } + heaterTransitionControl(heaterSwitchStateArray); + heaterMaxDurationControl(heaterSwitchStateArray); + // This dataset makes the TCS CTRL observable. + PoolReadGuard pg(&tcsCtrlInfo); + for (uint8_t i = 0; i < thermalStates.size(); i++) { + tcsCtrlInfo.heatingOnVec[i] = thermalStates[i].heating; + tcsCtrlInfo.sensorIdxUsedForTcsCtrl[i] = thermalStates[i].sensorIndex; + tcsCtrlInfo.heaterSwitchIdx[i] = thermalStates[i].heaterSwitch; + tcsCtrlInfo.heaterStartTimes[i] = thermalStates[i].heaterStartTime; + tcsCtrlInfo.heaterEndTimes[i] = thermalStates[i].heaterEndTime; + } } } @@ -277,6 +293,11 @@ ReturnValue_t ThermalController::initializeLocalDataPool(localpool::DataPool& lo localDataPoolMap.emplace(tcsCtrl::TEMP_ADC_PAYLOAD_PCDU, new PoolEntry({0.0})); localDataPoolMap.emplace(tcsCtrl::HEATER_SWITCH_LIST, &heaterSwitchStates); localDataPoolMap.emplace(tcsCtrl::HEATER_CURRENT, &heaterCurrent); + localDataPoolMap.emplace(tcsCtrl::HEATER_ON_FOR_COMPONENT_VEC, &tcsCtrlHeaterOn); + localDataPoolMap.emplace(tcsCtrl::SENSOR_USED_FOR_TCS_CTRL, &tcsCtrlSensorIdx); + localDataPoolMap.emplace(tcsCtrl::HEATER_IDX_USED_FOR_TCS_CTRL, &tcsCtrlHeaterIdx); + localDataPoolMap.emplace(tcsCtrl::HEATER_START_TIME, &tcsCtrlStartTimes); + localDataPoolMap.emplace(tcsCtrl::HEATER_END_TIME, &tcsCtrlEndTimes); bool enableHkSets = false; #if OBSW_ENABLE_PERIODIC_HK == 1 @@ -290,6 +311,8 @@ ReturnValue_t ThermalController::initializeLocalDataPool(localpool::DataPool& lo subdp::RegularHkPeriodicParams(deviceTemperatures.getSid(), enableHkSets, 120.0)); poolManager.subscribeForDiagPeriodicPacket( subdp::DiagnosticsHkPeriodicParams(heaterInfo.getSid(), enableHkSets, 120.0)); + poolManager.subscribeForRegularPeriodicPacket( + subdp::RegularHkPeriodicParams(tcsCtrlInfo.getSid(), enableHkSets, 120.0)); return returnvalue::OK; } @@ -304,6 +327,8 @@ LocalPoolDataSetBase* ThermalController::getDataSetHandle(sid_t sid) { return &deviceTemperatures; case tcsCtrl::HEATER_SET: return &heaterInfo; + case tcsCtrl::TCS_CTRL_INFO: + return &tcsCtrlInfo; default: return nullptr; } @@ -536,19 +561,19 @@ void ThermalController::copySensors() { } } } - // damaged - /* - { - PoolReadGuard pg(&tmp1075SetPlPcdu1, MutexIF::TimeoutType::WAITING, MUTEX_TIMEOUT); - if (pg.getReadResult() == returnvalue::OK) { - sensorTemperatures.tmp1075PlPcdu1.value = tmp1075SetPlPcdu1.temperatureCelcius.value; - sensorTemperatures.tmp1075PlPcdu1.setValid(tmp1075SetPlPcdu1.temperatureCelcius.isValid()); - if (not tmp1075SetPlPcdu1.temperatureCelcius.isValid()) { - sensorTemperatures.tmp1075PlPcdu1.value = INVALID_TEMPERATURE; + // damaged on FM, and no dummies for now + if (pollPcdu1Tmp) { + { + PoolReadGuard pg(tmp1075SetPlPcdu1, MutexIF::TimeoutType::WAITING, MUTEX_TIMEOUT); + if (pg.getReadResult() == returnvalue::OK) { + sensorTemperatures.tmp1075PlPcdu1.value = tmp1075SetPlPcdu1->temperatureCelcius.value; + sensorTemperatures.tmp1075PlPcdu1.setValid(tmp1075SetPlPcdu1->temperatureCelcius.isValid()); + if (not tmp1075SetPlPcdu1->temperatureCelcius.isValid()) { + sensorTemperatures.tmp1075PlPcdu1.value = INVALID_TEMPERATURE; + } } } } - */ { PoolReadGuard pg(&tmp1075SetIfBoard, MutexIF::TimeoutType::WAITING, MUTEX_TIMEOUT); if (pg.getReadResult() == returnvalue::OK) { @@ -1005,7 +1030,7 @@ void ThermalController::ctrlAcsBoard() { heater::Switch redSwitchNr = heater::HEATER_3_OBC_BRD; // A side - thermalComponent = ACS_BOARD; + ctrlCtx.thermalComponent = tcsCtrl::ACS_BOARD; sensors[0].first = deviceTemperatures.gyro0SideA.isValid(); sensors[0].second = deviceTemperatures.gyro0SideA.value; sensors[1].first = deviceTemperatures.gyro2SideB.isValid(); @@ -1049,7 +1074,7 @@ void ThermalController::ctrlAcsBoard() { if (chooseHeater(switchNr, redSwitchNr)) { if (heaterHandler.getSwitchState(switchNr)) { if (submode != SUBMODE_NO_HEATER_CTRL) { - heaterSwitchHelper(switchNr, HeaterHandler::SwitchState::OFF, thermalComponent); + heaterSwitchHelper(switchNr, heater::SwitchState::OFF, ctrlCtx.thermalComponent); } } } @@ -1059,7 +1084,7 @@ void ThermalController::ctrlAcsBoard() { } void ThermalController::ctrlMgt() { - thermalComponent = MGT; + ctrlCtx.thermalComponent = tcsCtrl::MGT; sensors[0].first = sensorTemperatures.mgt.isValid(); sensors[0].second = sensorTemperatures.mgt.value; sensors[1].first = deviceTemperatures.mgt.isValid(); @@ -1069,11 +1094,11 @@ void ThermalController::ctrlMgt() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_2_ACS_BRD, heater::HEATER_1_PCDU_PDU, mgtLimits); ctrlComponentTemperature(htrCtx); - if (componentAboveUpperLimit and not mgtTooHotFlag) { + if (ctrlCtx.componentAboveUpperLimit and not tooHotFlags.mgtTooHotFlag) { triggerEvent(tcsCtrl::MGT_OVERHEATING, tempFloatToU32()); - mgtTooHotFlag = true; - } else if (not componentAboveUpperLimit) { - mgtTooHotFlag = false; + tooHotFlags.mgtTooHotFlag = true; + } else if (not ctrlCtx.componentAboveUpperLimit) { + tooHotFlags.mgtTooHotFlag = false; } } @@ -1084,7 +1109,7 @@ void ThermalController::ctrlRw() { std::array sensorTemps{}; // RW1 - thermalComponent = RW; + ctrlCtx.thermalComponent = tcsCtrl::RW; sensors[0].first = sensorTemperatures.rw1.isValid(); sensors[0].second = sensorTemperatures.rw1.value; sensors[1].first = deviceTemperatures.rw1.isValid(); @@ -1098,14 +1123,14 @@ void ThermalController::ctrlRw() { HeaterContext htrCtx(heater::HEATER_6_DRO, heater::HEATER_6_DRO, rwLimits); ctrlComponentTemperature(htrCtx); sensorTemps[0] = tempFloatToU32(); - if (componentAboveUpperLimit) { + if (ctrlCtx.componentAboveUpperLimit) { oneIsAboveLimit = true; - eventToTrigger = overHeatEventToTrigger; + eventToTrigger = ctrlCtx.overHeatEventToTrigger; } } // RW2 - thermalComponent = RW; + ctrlCtx.thermalComponent = tcsCtrl::RW; sensors[0].first = deviceTemperatures.rw2.isValid(); sensors[0].second = deviceTemperatures.rw2.value; sensors[1].first = deviceTemperatures.rw3.isValid(); @@ -1119,15 +1144,15 @@ void ThermalController::ctrlRw() { HeaterContext htrCtx(heater::HEATER_6_DRO, heater::HEATER_6_DRO, rwLimits); ctrlComponentTemperature(htrCtx); sensorTemps[1] = tempFloatToU32(); - if (componentAboveUpperLimit) { + if (ctrlCtx.componentAboveUpperLimit) { oneIsAboveLimit = true; if (eventToTrigger != ThermalComponentIF::COMPONENT_TEMP_OOL_HIGH) { - eventToTrigger = overHeatEventToTrigger; + eventToTrigger = ctrlCtx.overHeatEventToTrigger; } } } // RW3 - thermalComponent = RW; + ctrlCtx.thermalComponent = tcsCtrl::RW; sensors[0].first = deviceTemperatures.rw3.isValid(); sensors[0].second = deviceTemperatures.rw3.value; sensors[1].first = deviceTemperatures.rw4.isValid(); @@ -1141,16 +1166,16 @@ void ThermalController::ctrlRw() { HeaterContext htrCtx(heater::HEATER_6_DRO, heater::HEATER_6_DRO, rwLimits); ctrlComponentTemperature(htrCtx); sensorTemps[2] = tempFloatToU32(); - if (componentAboveUpperLimit) { + if (ctrlCtx.componentAboveUpperLimit) { oneIsAboveLimit = true; if (eventToTrigger != ThermalComponentIF::COMPONENT_TEMP_OOL_HIGH) { - eventToTrigger = overHeatEventToTrigger; + eventToTrigger = ctrlCtx.overHeatEventToTrigger; } } } // RW4 - thermalComponent = RW; + ctrlCtx.thermalComponent = tcsCtrl::RW; sensors[0].first = deviceTemperatures.rw4.isValid(); sensors[0].second = deviceTemperatures.rw4.value; sensors[1].first = deviceTemperatures.rw1.isValid(); @@ -1164,27 +1189,27 @@ void ThermalController::ctrlRw() { HeaterContext htrCtx(heater::HEATER_6_DRO, heater::HEATER_6_DRO, rwLimits); ctrlComponentTemperature(htrCtx); sensorTemps[3] = tempFloatToU32(); - if (componentAboveUpperLimit) { + if (ctrlCtx.componentAboveUpperLimit) { oneIsAboveLimit = true; if (eventToTrigger != ThermalComponentIF::COMPONENT_TEMP_OOL_HIGH) { - eventToTrigger = overHeatEventToTrigger; + eventToTrigger = ctrlCtx.overHeatEventToTrigger; } } } - if (oneIsAboveLimit and not rwTooHotFlag) { + if (oneIsAboveLimit and not tooHotFlags.rwTooHotFlag) { EventManagerIF::triggerEvent(objects::RW1, eventToTrigger, sensorTemps[0]); EventManagerIF::triggerEvent(objects::RW2, eventToTrigger, sensorTemps[1]); EventManagerIF::triggerEvent(objects::RW3, eventToTrigger, sensorTemps[2]); EventManagerIF::triggerEvent(objects::RW4, eventToTrigger, sensorTemps[3]); - rwTooHotFlag = true; + tooHotFlags.rwTooHotFlag = true; } else if (not oneIsAboveLimit) { - rwTooHotFlag = false; + tooHotFlags.rwTooHotFlag = false; } } void ThermalController::ctrlStr() { - thermalComponent = STR; + ctrlCtx.thermalComponent = tcsCtrl::STR; sensors[0].first = sensorTemperatures.startracker.isValid(); sensors[0].second = sensorTemperatures.startracker.value; sensors[1].first = deviceTemperatures.startracker.isValid(); @@ -1194,11 +1219,11 @@ void ThermalController::ctrlStr() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_5_STR, heater::HEATER_6_DRO, strLimits); ctrlComponentTemperature(htrCtx); - tooHotHandlerWhichClearsOneShotFlag(objects::STAR_TRACKER, strTooHotFlag); + tooHotHandlerWhichClearsOneShotFlag(objects::STAR_TRACKER, tooHotFlags.strTooHotFlag); } void ThermalController::ctrlIfBoard() { - thermalComponent = IF_BOARD; + ctrlCtx.thermalComponent = tcsCtrl::IF_BOARD; sensors[0].first = sensorTemperatures.tmp1075IfBrd.isValid(); sensors[0].second = sensorTemperatures.tmp1075IfBrd.value; sensors[1].first = sensorTemperatures.mgt.isValid(); @@ -1212,7 +1237,7 @@ void ThermalController::ctrlIfBoard() { } void ThermalController::ctrlTcsBoard() { - thermalComponent = TCS_BOARD; + ctrlCtx.thermalComponent = tcsCtrl::TCS_BOARD; sensors[0].first = sensorTemperatures.tcsBoard.isValid(); sensors[0].second = sensorTemperatures.tcsBoard.value; sensors[1].first = sensorTemperatures.tmp1075Tcs0.isValid(); @@ -1226,7 +1251,7 @@ void ThermalController::ctrlTcsBoard() { } void ThermalController::ctrlObc() { - thermalComponent = OBC; + ctrlCtx.thermalComponent = tcsCtrl::OBC; sensors[0].first = deviceTemperatures.q7s.isValid(); sensors[0].second = deviceTemperatures.q7s.value; sensors[1].first = sensorTemperatures.tmp1075Tcs1.isValid(); @@ -1236,35 +1261,16 @@ void ThermalController::ctrlObc() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_3_OBC_BRD, heater::HEATER_2_ACS_BRD, obcLimits); ctrlComponentTemperature(htrCtx); - if (componentAboveUpperLimit and not obcTooHotFlag) { + if (ctrlCtx.componentAboveUpperLimit and not tooHotFlags.obcTooHotFlag) { triggerEvent(tcsCtrl::OBC_OVERHEATING, tempFloatToU32()); - obcTooHotFlag = true; - } else if (not componentAboveUpperLimit) { - obcTooHotFlag = false; - } -} - -void ThermalController::ctrlObcIfBoard() { - thermalComponent = OBCIF_BOARD; - sensors[0].first = deviceTemperatures.q7s.isValid(); - sensors[0].second = deviceTemperatures.q7s.value; - sensors[1].first = sensorTemperatures.tmp1075Tcs0.isValid(); - sensors[1].second = sensorTemperatures.tmp1075Tcs0.value; - sensors[2].first = sensorTemperatures.tmp1075Tcs1.isValid(); - sensors[2].second = sensorTemperatures.tmp1075Tcs1.value; - numSensors = 3; - HeaterContext htrCtx(heater::HEATER_3_OBC_BRD, heater::HEATER_2_ACS_BRD, obcIfBoardLimits); - ctrlComponentTemperature(htrCtx); - if (componentAboveUpperLimit and not obcTooHotFlag) { - triggerEvent(tcsCtrl::OBC_OVERHEATING, tempFloatToU32()); - obcTooHotFlag = true; - } else if (not componentAboveUpperLimit) { - obcTooHotFlag = false; + tooHotFlags.obcTooHotFlag = true; + } else if (not ctrlCtx.componentAboveUpperLimit) { + tooHotFlags.obcTooHotFlag = false; } } void ThermalController::ctrlSBandTransceiver() { - thermalComponent = SBAND_TRANSCEIVER; + ctrlCtx.thermalComponent = tcsCtrl::SBAND_TRANSCEIVER; sensors[0].first = deviceTemperatures.syrlinksPowerAmplifier.isValid(); sensors[0].second = deviceTemperatures.syrlinksPowerAmplifier.value; sensors[1].first = deviceTemperatures.syrlinksBasebandBoard.isValid(); @@ -1274,15 +1280,15 @@ void ThermalController::ctrlSBandTransceiver() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_7_S_BAND, heater::HEATER_4_CAMERA, sBandTransceiverLimits); ctrlComponentTemperature(htrCtx); - if (componentAboveUpperLimit and not syrlinksTooHotFlag) { + if (ctrlCtx.componentAboveUpperLimit and not tooHotFlags.syrlinksTooHotFlag) { triggerEvent(tcsCtrl::SYRLINKS_OVERHEATING, tempFloatToU32()); - syrlinksTooHotFlag = true; - } else if (not componentAboveUpperLimit) { - syrlinksTooHotFlag = false; + tooHotFlags.syrlinksTooHotFlag = true; + } else if (not ctrlCtx.componentAboveUpperLimit) { + tooHotFlags.syrlinksTooHotFlag = false; } } void ThermalController::ctrlPcduP60Board() { - thermalComponent = PCDUP60_BOARD; + ctrlCtx.thermalComponent = tcsCtrl::PCDUP60_BOARD; sensors[0].first = deviceTemperatures.temp1P60dock.isValid(); sensors[0].second = deviceTemperatures.temp1P60dock.value; sensors[1].first = deviceTemperatures.temp2P60dock.isValid(); @@ -1290,16 +1296,16 @@ void ThermalController::ctrlPcduP60Board() { numSensors = 2; HeaterContext htrCtx(heater::HEATER_1_PCDU_PDU, heater::HEATER_2_ACS_BRD, pcduP60BoardLimits); ctrlComponentTemperature(htrCtx); - if (componentAboveUpperLimit and not pcduSystemTooHotFlag) { + if (ctrlCtx.componentAboveUpperLimit and not tooHotFlags.pcduSystemTooHotFlag) { triggerEvent(tcsCtrl::PCDU_SYSTEM_OVERHEATING, tempFloatToU32()); - pcduSystemTooHotFlag = true; - } else if (not componentAboveUpperLimit) { - pcduSystemTooHotFlag = false; + tooHotFlags.pcduSystemTooHotFlag = true; + } else if (not ctrlCtx.componentAboveUpperLimit) { + tooHotFlags.pcduSystemTooHotFlag = false; } // TODO: ! } void ThermalController::ctrlPcduAcu() { - thermalComponent = PCDUACU; + ctrlCtx.thermalComponent = tcsCtrl::PCDUACU; heater::Switch switchNr = heater::HEATER_1_PCDU_PDU; heater::Switch redSwitchNr = heater::HEATER_2_ACS_BRD; @@ -1307,15 +1313,15 @@ void ThermalController::ctrlPcduAcu() { bool sensorTempAvailable = true; // TODO: check if (deviceTemperatures.acu.value[0] != INVALID_TEMPERATURE) { - sensorTemp = deviceTemperatures.acu.value[0]; + ctrlCtx.sensorTemp = deviceTemperatures.acu.value[0]; } else if (deviceTemperatures.acu.value[1] != INVALID_TEMPERATURE) { - sensorTemp = deviceTemperatures.acu.value[1]; + ctrlCtx.sensorTemp = deviceTemperatures.acu.value[1]; } else if (deviceTemperatures.acu.value[2] != INVALID_TEMPERATURE) { - sensorTemp = deviceTemperatures.acu.value[2]; + ctrlCtx.sensorTemp = deviceTemperatures.acu.value[2]; } else if (sensorTemperatures.acu.isValid()) { - sensorTemp = sensorTemperatures.acu.value; + ctrlCtx.sensorTemp = sensorTemperatures.acu.value; } else { - triggerEvent(tcsCtrl::NO_VALID_SENSOR_TEMPERATURE, thermalComponent); + triggerEvent(tcsCtrl::NO_VALID_SENSOR_TEMPERATURE, ctrlCtx.thermalComponent); sensorTempAvailable = false; } if (sensorTempAvailable) { @@ -1323,16 +1329,16 @@ void ThermalController::ctrlPcduAcu() { checkLimitsAndCtrlHeater(htrCtx); } } - if (componentAboveUpperLimit and not pcduSystemTooHotFlag) { + if (ctrlCtx.componentAboveUpperLimit and not tooHotFlags.pcduSystemTooHotFlag) { triggerEvent(tcsCtrl::PCDU_SYSTEM_OVERHEATING, tempFloatToU32()); - pcduSystemTooHotFlag = true; - } else if (not componentAboveUpperLimit) { - pcduSystemTooHotFlag = false; + tooHotFlags.pcduSystemTooHotFlag = true; + } else if (not ctrlCtx.componentAboveUpperLimit) { + tooHotFlags.pcduSystemTooHotFlag = false; } } void ThermalController::ctrlPcduPdu() { - thermalComponent = PCDUPDU; + ctrlCtx.thermalComponent = tcsCtrl::PCDUPDU; sensors[0].first = deviceTemperatures.pdu1.isValid(); sensors[0].second = deviceTemperatures.pdu1.value; sensors[1].first = deviceTemperatures.pdu2.isValid(); @@ -1342,16 +1348,16 @@ void ThermalController::ctrlPcduPdu() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_1_PCDU_PDU, heater::HEATER_2_ACS_BRD, pcduPduLimits); ctrlComponentTemperature(htrCtx); - if (componentAboveUpperLimit and not pcduSystemTooHotFlag) { + if (ctrlCtx.componentAboveUpperLimit and not tooHotFlags.pcduSystemTooHotFlag) { triggerEvent(tcsCtrl::PCDU_SYSTEM_OVERHEATING, tempFloatToU32()); - pcduSystemTooHotFlag = true; - } else if (not componentAboveUpperLimit) { - pcduSystemTooHotFlag = false; + tooHotFlags.pcduSystemTooHotFlag = true; + } else if (not ctrlCtx.componentAboveUpperLimit) { + tooHotFlags.pcduSystemTooHotFlag = false; } } void ThermalController::ctrlPlPcduBoard() { - thermalComponent = PLPCDU_BOARD; + ctrlCtx.thermalComponent = tcsCtrl::PLPCDU_BOARD; sensors[0].first = sensorTemperatures.tmp1075PlPcdu0.isValid(); sensors[0].second = sensorTemperatures.tmp1075PlPcdu0.value; sensors[1].first = sensorTemperatures.tmp1075PlPcdu1.isValid(); @@ -1363,11 +1369,11 @@ void ThermalController::ctrlPlPcduBoard() { numSensors = 4; HeaterContext htrCtx(heater::HEATER_1_PCDU_PDU, heater::HEATER_2_ACS_BRD, plPcduBoardLimits); ctrlComponentTemperature(htrCtx); - tooHotHandler(objects::PLPCDU_HANDLER, eBandTooHotFlag); + tooHotHandler(objects::PLPCDU_HANDLER, tooHotFlags.eBandTooHotFlag); } void ThermalController::ctrlPlocMissionBoard() { - thermalComponent = PLOCMISSION_BOARD; + ctrlCtx.thermalComponent = tcsCtrl::PLOCMISSION_BOARD; sensors[0].first = sensorTemperatures.plocHeatspreader.isValid(); sensors[0].second = sensorTemperatures.plocHeatspreader.value; sensors[1].first = sensorTemperatures.plocMissionboard.isValid(); @@ -1378,11 +1384,11 @@ void ThermalController::ctrlPlocMissionBoard() { HeaterContext htrCtx(heater::HEATER_0_PLOC_PROC_BRD, heater::HEATER_3_OBC_BRD, plocMissionBoardLimits); ctrlComponentTemperature(htrCtx); - tooHotHandler(objects::PLOC_SUPERVISOR_HANDLER, plocTooHotFlag); + tooHotHandler(objects::PLOC_SUPERVISOR_HANDLER, tooHotFlags.plocTooHotFlag); } void ThermalController::ctrlPlocProcessingBoard() { - thermalComponent = PLOCPROCESSING_BOARD; + ctrlCtx.thermalComponent = tcsCtrl::PLOCPROCESSING_BOARD; sensors[0].first = sensorTemperatures.plocMissionboard.isValid(); sensors[0].second = sensorTemperatures.plocMissionboard.value; sensors[1].first = sensorTemperatures.plocHeatspreader.isValid(); @@ -1393,11 +1399,11 @@ void ThermalController::ctrlPlocProcessingBoard() { HeaterContext htrCtx(heater::HEATER_0_PLOC_PROC_BRD, heater::HEATER_3_OBC_BRD, plocProcessingBoardLimits); ctrlComponentTemperature(htrCtx); - tooHotHandler(objects::PLOC_SUPERVISOR_HANDLER, plocTooHotFlag); + tooHotHandler(objects::PLOC_SUPERVISOR_HANDLER, tooHotFlags.plocTooHotFlag); } void ThermalController::ctrlDac() { - thermalComponent = DAC; + ctrlCtx.thermalComponent = tcsCtrl::DAC; sensors[0].first = sensorTemperatures.dacHeatspreader.isValid(); sensors[0].second = sensorTemperatures.dacHeatspreader.value; sensors[1].first = sensorTemperatures.plocMissionboard.isValid(); @@ -1407,11 +1413,11 @@ void ThermalController::ctrlDac() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_0_PLOC_PROC_BRD, heater::HEATER_3_OBC_BRD, dacLimits); ctrlComponentTemperature(htrCtx); - tooHotHandler(objects::PLPCDU_HANDLER, eBandTooHotFlag); + tooHotHandler(objects::PLPCDU_HANDLER, tooHotFlags.eBandTooHotFlag); } void ThermalController::ctrlCameraBody() { - thermalComponent = CAMERA; + ctrlCtx.thermalComponent = tcsCtrl::CAMERA; sensors[0].first = sensorTemperatures.payload4kCamera.isValid(); sensors[0].second = sensorTemperatures.payload4kCamera.value; sensors[1].first = sensorTemperatures.dro.isValid(); @@ -1421,7 +1427,7 @@ void ThermalController::ctrlCameraBody() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_4_CAMERA, heater::HEATER_6_DRO, cameraLimits); ctrlComponentTemperature(htrCtx); - if (componentAboveUpperLimit and not camTooHotOneShotFlag) { + if (ctrlCtx.componentAboveUpperLimit and not tooHotFlags.camTooHotOneShotFlag) { triggerEvent(tcsCtrl::CAMERA_OVERHEATING, tempFloatToU32()); CommandMessage msg; HealthMessage::setHealthMessage(&msg, HealthMessage::HEALTH_SET, HealthState::FAULTY); @@ -1430,14 +1436,14 @@ void ThermalController::ctrlCameraBody() { sif::error << "ThermalController::ctrlCameraBody(): Sending health message failed" << std::endl; } - camTooHotOneShotFlag = true; - } else if (not componentAboveUpperLimit) { - camTooHotOneShotFlag = false; + tooHotFlags.camTooHotOneShotFlag = true; + } else if (not ctrlCtx.componentAboveUpperLimit) { + tooHotFlags.camTooHotOneShotFlag = false; } } void ThermalController::ctrlDro() { - thermalComponent = DRO; + ctrlCtx.thermalComponent = tcsCtrl::DRO; sensors[0].first = sensorTemperatures.dro.isValid(); sensors[0].second = sensorTemperatures.dro.value; sensors[1].first = sensorTemperatures.payload4kCamera.isValid(); @@ -1447,11 +1453,11 @@ void ThermalController::ctrlDro() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_6_DRO, heater::HEATER_4_CAMERA, droLimits); ctrlComponentTemperature(htrCtx); - tooHotHandler(objects::PLPCDU_HANDLER, eBandTooHotFlag); + tooHotHandler(objects::PLPCDU_HANDLER, tooHotFlags.eBandTooHotFlag); } void ThermalController::ctrlX8() { - thermalComponent = X8; + ctrlCtx.thermalComponent = tcsCtrl::X8; sensors[0].first = sensorTemperatures.x8.isValid(); sensors[0].second = sensorTemperatures.x8.value; sensors[1].first = sensorTemperatures.hpa.isValid(); @@ -1461,11 +1467,11 @@ void ThermalController::ctrlX8() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_6_DRO, heater::HEATER_4_CAMERA, x8Limits); ctrlComponentTemperature(htrCtx); - tooHotHandler(objects::PLPCDU_HANDLER, eBandTooHotFlag); + tooHotHandler(objects::PLPCDU_HANDLER, tooHotFlags.eBandTooHotFlag); } void ThermalController::ctrlTx() { - thermalComponent = TX; + ctrlCtx.thermalComponent = tcsCtrl::TX; sensors[0].first = sensorTemperatures.eBandTx.isValid(); sensors[0].second = sensorTemperatures.eBandTx.value; sensors[1].first = sensorTemperatures.x8.isValid(); @@ -1475,11 +1481,11 @@ void ThermalController::ctrlTx() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_6_DRO, heater::HEATER_4_CAMERA, txLimits); ctrlComponentTemperature(htrCtx); - tooHotHandler(objects::PLPCDU_HANDLER, eBandTooHotFlag); + tooHotHandler(objects::PLPCDU_HANDLER, tooHotFlags.eBandTooHotFlag); } void ThermalController::ctrlMpa() { - thermalComponent = MPA; + ctrlCtx.thermalComponent = tcsCtrl::MPA; sensors[0].first = sensorTemperatures.mpa.isValid(); sensors[0].second = sensorTemperatures.mpa.value; sensors[1].first = sensorTemperatures.hpa.isValid(); @@ -1489,11 +1495,11 @@ void ThermalController::ctrlMpa() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_6_DRO, heater::HEATER_4_CAMERA, mpaLimits); ctrlComponentTemperature(htrCtx); - tooHotHandler(objects::PLPCDU_HANDLER, eBandTooHotFlag); + tooHotHandler(objects::PLPCDU_HANDLER, tooHotFlags.eBandTooHotFlag); } void ThermalController::ctrlHpa() { - thermalComponent = HPA; + ctrlCtx.thermalComponent = tcsCtrl::HPA; sensors[0].first = sensorTemperatures.hpa.isValid(); sensors[0].second = sensorTemperatures.hpa.value; sensors[1].first = sensorTemperatures.x8.isValid(); @@ -1503,11 +1509,11 @@ void ThermalController::ctrlHpa() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_6_DRO, heater::HEATER_4_CAMERA, hpaLimits); ctrlComponentTemperature(htrCtx); - tooHotHandler(objects::PLPCDU_HANDLER, eBandTooHotFlag); + tooHotHandler(objects::PLPCDU_HANDLER, tooHotFlags.eBandTooHotFlag); } void ThermalController::ctrlScexBoard() { - thermalComponent = SCEX_BOARD; + ctrlCtx.thermalComponent = tcsCtrl::SCEX_BOARD; sensors[0].first = sensorTemperatures.scex.isValid(); sensors[0].second = sensorTemperatures.scex.value; sensors[1].first = sensorTemperatures.x8.isValid(); @@ -1517,10 +1523,11 @@ void ThermalController::ctrlScexBoard() { numSensors = 3; HeaterContext htrCtx(heater::HEATER_6_DRO, heater::HEATER_5_STR, scexBoardLimits); ctrlComponentTemperature(htrCtx); - tooHotHandlerWhichClearsOneShotFlag(objects::SCEX, scexTooHotFlag); + tooHotHandlerWhichClearsOneShotFlag(objects::SCEX, tooHotFlags.scexTooHotFlag); } -void ThermalController::performThermalModuleCtrl(const HeaterSwitchStates& heaterSwitchStates) { +void ThermalController::performThermalModuleCtrl( + const tcsCtrl::HeaterSwitchStates& heaterSwitchStates) { ctrlAcsBoard(); ctrlMgt(); ctrlRw(); @@ -1528,7 +1535,6 @@ void ThermalController::performThermalModuleCtrl(const HeaterSwitchStates& heate ctrlIfBoard(); ctrlTcsBoard(); ctrlObc(); - ctrlObcIfBoard(); ctrlSBandTransceiver(); ctrlPcduP60Board(); ctrlPcduAcu(); @@ -1537,11 +1543,11 @@ void ThermalController::performThermalModuleCtrl(const HeaterSwitchStates& heate // Payload components std::array plocInAllowedRange{}; ctrlPlocMissionBoard(); - plocInAllowedRange.at(0) = not componentAboveUpperLimit; + plocInAllowedRange.at(0) = not ctrlCtx.componentAboveUpperLimit; ctrlPlocProcessingBoard(); - plocInAllowedRange.at(1) = not componentAboveUpperLimit; + plocInAllowedRange.at(1) = not ctrlCtx.componentAboveUpperLimit; - if (plocTooHotFlag) { + if (tooHotFlags.plocTooHotFlag) { bool clearFlag = true; for (const auto& inRange : plocInAllowedRange) { if (not inRange) { @@ -1549,7 +1555,7 @@ void ThermalController::performThermalModuleCtrl(const HeaterSwitchStates& heate } } if (clearFlag) { - plocTooHotFlag = false; + tooHotFlags.plocTooHotFlag = false; } } ctrlCameraBody(); @@ -1558,21 +1564,21 @@ void ThermalController::performThermalModuleCtrl(const HeaterSwitchStates& heate // E-Band std::array eBandInAllowedRange{}; ctrlPlPcduBoard(); - eBandInAllowedRange.at(0) = not componentAboveUpperLimit; + eBandInAllowedRange.at(0) = not ctrlCtx.componentAboveUpperLimit; ctrlDac(); - eBandInAllowedRange.at(1) = not componentAboveUpperLimit; + eBandInAllowedRange.at(1) = not ctrlCtx.componentAboveUpperLimit; ctrlDro(); - eBandInAllowedRange.at(2) = not componentAboveUpperLimit; + eBandInAllowedRange.at(2) = not ctrlCtx.componentAboveUpperLimit; ctrlX8(); - eBandInAllowedRange.at(3) = not componentAboveUpperLimit; + eBandInAllowedRange.at(3) = not ctrlCtx.componentAboveUpperLimit; ctrlHpa(); - eBandInAllowedRange.at(4) = not componentAboveUpperLimit; + eBandInAllowedRange.at(4) = not ctrlCtx.componentAboveUpperLimit; ctrlTx(); - eBandInAllowedRange.at(5) = not componentAboveUpperLimit; + eBandInAllowedRange.at(5) = not ctrlCtx.componentAboveUpperLimit; ctrlMpa(); - eBandInAllowedRange.at(6) = not componentAboveUpperLimit; + eBandInAllowedRange.at(6) = not ctrlCtx.componentAboveUpperLimit; - if (eBandTooHotFlag) { + if (tooHotFlags.eBandTooHotFlag) { bool clearFlag = true; for (const auto& inRange : eBandInAllowedRange) { if (not inRange) { @@ -1580,12 +1586,11 @@ void ThermalController::performThermalModuleCtrl(const HeaterSwitchStates& heate } } if (clearFlag) { - eBandTooHotFlag = false; + tooHotFlags.eBandTooHotFlag = false; } } - - heaterTransitionControl(heaterSwitchStates); } + void ThermalController::ctrlComponentTemperature(HeaterContext& htrCtx) { if (selectAndReadSensorTemp(htrCtx)) { if (chooseHeater(htrCtx.switchNr, htrCtx.redSwitchNr)) { @@ -1596,9 +1601,11 @@ void ThermalController::ctrlComponentTemperature(HeaterContext& htrCtx) { // No sensors available, so switch the heater off. We can not perform control tasks if we // are blind.. if (chooseHeater(htrCtx.switchNr, htrCtx.redSwitchNr)) { + // Also track the counter to prevent heater handler message spam. The heater handle can only + // process 2 messages per cycle. if (heaterCtrlAllowed() and - (heaterHandler.getSwitchState(htrCtx.switchNr) == HeaterHandler::SwitchState::ON)) { - heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::OFF, thermalComponent); + (thermalStates[ctrlCtx.thermalComponent].noSensorAvailableCounter < 3)) { + heaterSwitchHelper(htrCtx.switchNr, heater::SwitchState::OFF, ctrlCtx.thermalComponent); } } } @@ -1609,20 +1616,21 @@ bool ThermalController::selectAndReadSensorTemp(HeaterContext& htrCtx) { if (sensors[i].first and sensors[i].second != INVALID_TEMPERATURE and sensors[i].second > SANITY_LIMIT_LOWER_TEMP and sensors[i].second < SANITY_LIMIT_UPPER_TEMP) { - sensorTemp = sensors[i].second; - thermalStates[thermalComponent].errorCounter = 0; + ctrlCtx.sensorTemp = sensors[i].second; + ctrlCtx.currentSensorIndex = i; + thermalStates[ctrlCtx.thermalComponent].noSensorAvailableCounter = 0; return true; } } - thermalStates[thermalComponent].errorCounter++; - if (thermalComponent != RW and thermalComponent != ACS_BOARD) { - if (thermalStates[thermalComponent].errorCounter <= 3) { - triggerEvent(tcsCtrl::NO_VALID_SENSOR_TEMPERATURE, thermalComponent); + thermalStates[ctrlCtx.thermalComponent].noSensorAvailableCounter++; + if (ctrlCtx.thermalComponent != tcsCtrl::RW and ctrlCtx.thermalComponent != tcsCtrl::ACS_BOARD) { + if (thermalStates[ctrlCtx.thermalComponent].noSensorAvailableCounter <= 3) { + triggerEvent(tcsCtrl::NO_VALID_SENSOR_TEMPERATURE, ctrlCtx.thermalComponent); } } else { - if (thermalStates[thermalComponent].errorCounter <= 8) { - triggerEvent(tcsCtrl::NO_VALID_SENSOR_TEMPERATURE, thermalComponent); + if (thermalStates[ctrlCtx.thermalComponent].noSensorAvailableCounter <= 8) { + triggerEvent(tcsCtrl::NO_VALID_SENSOR_TEMPERATURE, ctrlCtx.thermalComponent); } } @@ -1636,7 +1644,7 @@ bool ThermalController::chooseHeater(heater::Switch& switchNr, heater::Switch re if (mainHealth != HasHealthIF::HEALTHY) { if (redHealth == HasHealthIF::HEALTHY) { switchNr = redSwitchNr; - redSwitchNrInUse = true; + ctrlCtx.redSwitchNrInUse = true; } else { heaterAvailable = false; // Special case: Ground might command/do something with the heaters, so prevent spam. @@ -1645,7 +1653,7 @@ bool ThermalController::chooseHeater(heater::Switch& switchNr, heater::Switch re } } } else { - redSwitchNrInUse = false; + ctrlCtx.redSwitchNrInUse = false; } return heaterAvailable; } @@ -1654,23 +1662,23 @@ void ThermalController::heaterCtrlTempTooHighHandler(HeaterContext& htrCtx, cons if (not heaterCtrlAllowed()) { return; } - if (htrCtx.switchState == HeaterHandler::SwitchState::ON) { - sif::info << "TCS: Component " << static_cast(thermalComponent) << " too warm, above " - << whatLimit << ", switching off heater" << std::endl; - heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::OFF, thermalComponent); + if (htrCtx.switchState == heater::SwitchState::ON) { + sif::info << "TCS: Component " << static_cast(ctrlCtx.thermalComponent) + << " too warm, above " << whatLimit << ", switching off heater" << std::endl; + heaterSwitchHelper(htrCtx.switchNr, heater::SwitchState::OFF, ctrlCtx.thermalComponent); heaterStates[htrCtx.switchNr].switchTransition = true; - heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::OFF; + heaterStates[htrCtx.switchNr].target = heater::SwitchState::OFF; } - if (heaterHandler.getSwitchState(htrCtx.redSwitchNr) == HeaterHandler::SwitchState::ON) { - heaterSwitchHelper(htrCtx.redSwitchNr, HeaterHandler::SwitchState::OFF, thermalComponent); + if (heaterHandler.getSwitchState(htrCtx.redSwitchNr) == heater::SwitchState::ON) { + heaterSwitchHelper(htrCtx.redSwitchNr, heater::SwitchState::OFF, ctrlCtx.thermalComponent); heaterStates[htrCtx.redSwitchNr].switchTransition = true; - heaterStates[htrCtx.redSwitchNr].target = HeaterHandler::SwitchState::OFF; + heaterStates[htrCtx.redSwitchNr].target = heater::SwitchState::OFF; } } void ThermalController::checkLimitsAndCtrlHeater(HeaterContext& htrCtx) { - componentAboveCutOffLimit = false; - componentAboveUpperLimit = false; + ctrlCtx.componentAboveCutOffLimit = false; + ctrlCtx.componentAboveUpperLimit = false; // Stay passive during switch transitions, wait for heater switching to complete. Otherwise, // still check whether components are out of range, which might be important information for the // top level control loop. @@ -1680,39 +1688,41 @@ void ThermalController::checkLimitsAndCtrlHeater(HeaterContext& htrCtx) { return; } - htrCtx.switchState = heaterHandler.getSwitchState(htrCtx.switchNr); + htrCtx.switchState = + static_cast(heaterInfo.heaterSwitchState[htrCtx.switchNr]); // Heater off - if (htrCtx.switchState == HeaterHandler::SwitchState::OFF) { - if (sensorTemp < htrCtx.tempLimit.opLowerLimit and heaterCtrlAllowed()) { - sif::info << "TCS: Heater " << static_cast(thermalComponent) << " ON" << std::endl; - heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::ON, thermalComponent); - heaterStates[htrCtx.switchNr].switchTransition = true; - heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::ON; + if (htrCtx.switchState == heater::SwitchState::OFF) { + if (ctrlCtx.sensorTemp < htrCtx.tempLimit.opLowerLimit and heaterCtrlAllowed()) { + sif::info << "TCS: Heater " << static_cast(ctrlCtx.thermalComponent) << " ON" + << std::endl; + heaterSwitchHelper(htrCtx.switchNr, heater::SwitchState::ON, ctrlCtx.thermalComponent); } else { // Even if heater control is now allowed, we can update the state. - thermalStates[thermalComponent].heating = false; + thermalStates[ctrlCtx.thermalComponent].heating = false; } heaterCtrlCheckUpperLimits(htrCtx); return; } // Heater on - if (htrCtx.switchState == HeaterHandler::SwitchState::ON) { - if (thermalStates[thermalComponent].heating) { + if (htrCtx.switchState == heater::SwitchState::ON) { + if (thermalStates[ctrlCtx.thermalComponent].heating) { // We are already in a heating cycle, so need to check whether heating task is complete. - if (sensorTemp >= htrCtx.tempLimit.opLowerLimit + TEMP_OFFSET and heaterCtrlAllowed()) { - sif::info << "TCS: Heater " << static_cast(thermalComponent) << " OFF" << std::endl; - heaterSwitchHelper(htrCtx.switchNr, HeaterHandler::SwitchState::OFF, thermalComponent); + if (ctrlCtx.sensorTemp >= htrCtx.tempLimit.opLowerLimit + TEMP_OFFSET and + heaterCtrlAllowed()) { + sif::info << "TCS: Heater " << static_cast(ctrlCtx.thermalComponent) << " OFF" + << std::endl; + heaterSwitchHelper(htrCtx.switchNr, heater::SwitchState::OFF, ctrlCtx.thermalComponent); heaterStates[htrCtx.switchNr].switchTransition = true; - heaterStates[htrCtx.switchNr].target = HeaterHandler::SwitchState::OFF; + heaterStates[htrCtx.switchNr].target = heater::SwitchState::OFF; } return; } // This can happen if heater is used as alternative heater (no regular heating cycle), so we // should still check the upper limits. bool tooHighHandlerAlreadyCalled = heaterCtrlCheckUpperLimits(htrCtx); - if (sensorTemp >= htrCtx.tempLimit.cutOffLimit) { - componentAboveCutOffLimit = true; + if (ctrlCtx.sensorTemp >= htrCtx.tempLimit.cutOffLimit) { + ctrlCtx.componentAboveCutOffLimit = true; if (not tooHighHandlerAlreadyCalled) { heaterCtrlTempTooHighHandler(htrCtx, "CutOff-Limit"); } @@ -1721,19 +1731,19 @@ void ThermalController::checkLimitsAndCtrlHeater(HeaterContext& htrCtx) { } bool ThermalController::heaterCtrlCheckUpperLimits(HeaterContext& htrCtx) { - if (sensorTemp >= htrCtx.tempLimit.nopUpperLimit) { - componentAboveUpperLimit = true; + if (ctrlCtx.sensorTemp >= htrCtx.tempLimit.nopUpperLimit) { + ctrlCtx.componentAboveUpperLimit = true; if (htrCtx.doHeaterHandling) { heaterCtrlTempTooHighHandler(htrCtx, "NOP-Limit"); } - overHeatEventToTrigger = ThermalComponentIF::COMPONENT_TEMP_OOL_HIGH; + ctrlCtx.overHeatEventToTrigger = ThermalComponentIF::COMPONENT_TEMP_OOL_HIGH; return true; - } else if (sensorTemp >= htrCtx.tempLimit.opUpperLimit) { - componentAboveUpperLimit = true; + } else if (ctrlCtx.sensorTemp >= htrCtx.tempLimit.opUpperLimit) { + ctrlCtx.componentAboveUpperLimit = true; if (htrCtx.doHeaterHandling) { heaterCtrlTempTooHighHandler(htrCtx, "OP-Limit"); } - overHeatEventToTrigger = ThermalComponentIF::COMPONENT_TEMP_HIGH; + ctrlCtx.overHeatEventToTrigger = ThermalComponentIF::COMPONENT_TEMP_HIGH; return true; } return false; @@ -1744,17 +1754,30 @@ void ThermalController::resetSensorsArray() { validValuePair.first = false; validValuePair.second = INVALID_TEMPERATURE; } - thermalComponent = NONE; + ctrlCtx.thermalComponent = tcsCtrl::NONE; } -void ThermalController::heaterTransitionControl(const HeaterSwitchStates& currentHeaterStates) { - for (unsigned i = 0; i < 7; i++) { +void ThermalController::heaterTransitionControl( + const tcsCtrl::HeaterSwitchStates& currentHeaterStates) { + for (unsigned i = 0; i < heater::Switch::NUMBER_OF_SWITCHES; i++) { if (heaterStates[i].switchTransition) { if (currentHeaterStates[i] == heaterStates[i].target) { + // Required for max heater period control + if (currentHeaterStates[i] == heater::SwitchState::ON) { + heaterStates[i].heaterOnMaxBurnTime.setTimeout(MAX_HEATER_ON_DURATIONS_MS[i]); + heaterStates[i].heaterOnMaxBurnTime.resetTimer(); + heaterStates[i].trackHeaterMaxBurnTime = true; + } else { + heaterStates[i].trackHeaterMaxBurnTime = false; + // The heater might still be one for some thermal components, so cross-check + // those components + crossCheckHeaterStateOfComponentsWhenHeaterGoesOff(static_cast(i)); + } heaterStates[i].switchTransition = false; + heaterStates[i].heaterSwitchControlCycles = 0; continue; } - if (heaterStates[i].heaterSwitchControlCycles > 3) { + if (heaterStates[i].heaterSwitchControlCycles > 5) { heaterStates[i].switchTransition = false; heaterStates[i].heaterSwitchControlCycles = 0; } @@ -1762,8 +1785,30 @@ void ThermalController::heaterTransitionControl(const HeaterSwitchStates& curren } } } + +void ThermalController::heaterMaxDurationControl( + const tcsCtrl::HeaterSwitchStates& currentHeaterStates) { + for (unsigned i = 0; i < heater::Switch::NUMBER_OF_SWITCHES; i++) { + // Right now, we only track the maximum duration for heater which were commanded by the TCS + // controller. + if (currentHeaterStates[i] == heater::SwitchState::ON and + heaterStates[i].trackHeaterMaxBurnTime and + heaterStates[i].heaterOnMaxBurnTime.hasTimedOut()) { + heaterStates[i].switchTransition = false; + heaterStates[i].heaterSwitchControlCycles = 0; + heaterStates[i].trackHeaterMaxBurnTime = false; + triggerEvent(tcsCtrl::TCS_HEATER_MAX_BURN_TIME_REACHED, static_cast(i), + MAX_HEATER_ON_DURATIONS_MS[i]); + heaterSwitchHelper(static_cast(i), heater::SwitchState::OFF, std::nullopt); + // The heater might still be one for some thermal components, so cross-check + // those components + crossCheckHeaterStateOfComponentsWhenHeaterGoesOff(static_cast(i)); + } + } +} + uint32_t ThermalController::tempFloatToU32() const { - auto sensorTempAsFloat = static_cast(sensorTemp); + auto sensorTempAsFloat = static_cast(ctrlCtx.sensorTemp); uint32_t tempRaw = 0; size_t dummyLen = 0; SerializeAdapter::serialize(&sensorTempAsFloat, reinterpret_cast(&tempRaw), &dummyLen, @@ -1782,9 +1827,9 @@ void ThermalController::setMode(Mode_t mode, Submode_t submode) { } bool ThermalController::tooHotHandler(object_id_t object, bool& oneShotFlag) { - if (componentAboveUpperLimit and not oneShotFlag) { + if (ctrlCtx.componentAboveUpperLimit and not oneShotFlag) { // Too hot -> returns true - EventManagerIF::triggerEvent(object, overHeatEventToTrigger, tempFloatToU32()); + EventManagerIF::triggerEvent(object, ctrlCtx.overHeatEventToTrigger, tempFloatToU32()); oneShotFlag = true; return true; } @@ -1796,31 +1841,48 @@ bool ThermalController::heaterCtrlAllowed() const { return submode != SUBMODE_NO void ThermalController::resetThermalStates() { for (auto& thermalState : thermalStates) { thermalState.heating = false; + thermalState.noSensorAvailableCounter = 0; + thermalState.heaterStartTime = 0; + thermalState.heaterEndTime = 0; + thermalState.sensorIndex = 0; + thermalState.heaterSwitch = heater::Switch::NUMBER_OF_SWITCHES; } } -void ThermalController::heaterSwitchHelper(heater::Switch switchNr, - HeaterHandler::SwitchState state, - unsigned componentIdx) { +void ThermalController::heaterSwitchHelper(heater::Switch switchNr, heater::SwitchState targetState, + std::optional componentIdx) { timeval currentTime; - Clock::getClockMonotonic(¤tTime); - if (state == HeaterHandler::SwitchState::ON) { - heaterHandler.switchHeater(switchNr, state); - thermalStates[componentIdx].heating = true; - thermalStates[componentIdx].heaterStartTime = currentTime.tv_sec; + Clock::getClock(¤tTime); + if (targetState == heater::SwitchState::ON) { + heaterHandler.switchHeater(switchNr, targetState); + heaterStates[switchNr].target = heater::SwitchState::ON; + heaterStates[switchNr].switchTransition = true; + if (componentIdx.has_value()) { + unsigned componentIdxVal = componentIdx.value(); + thermalStates[componentIdxVal].sensorIndex = ctrlCtx.currentSensorIndex; + thermalStates[componentIdxVal].heaterSwitch = switchNr; + thermalStates[componentIdxVal].heating = true; + thermalStates[componentIdxVal].heaterStartTime = currentTime.tv_sec; + } + triggerEvent(tcsCtrl::TCS_SWITCHING_HEATER_ON, static_cast(ctrlCtx.thermalComponent), + static_cast(switchNr)); } else { - heaterHandler.switchHeater(switchNr, state); - thermalStates[componentIdx].heating = false; - thermalStates[componentIdx].heaterEndTime = currentTime.tv_sec; + heaterHandler.switchHeater(switchNr, targetState); + if (componentIdx.has_value()) { + thermalStates[componentIdx.value()].heating = false; + thermalStates[componentIdx.value()].heaterEndTime = currentTime.tv_sec; + } + triggerEvent(tcsCtrl::TCS_SWITCHING_HEATER_OFF, static_cast(ctrlCtx.thermalComponent), + static_cast(switchNr)); } } void ThermalController::heaterSwitchHelperAllOff() { timeval currentTime; - Clock::getClockMonotonic(¤tTime); + Clock::getClock(¤tTime); size_t idx = 0; for (; idx < heater::Switch::NUMBER_OF_SWITCHES; idx++) { - heaterHandler.switchHeater(static_cast(idx), HeaterHandler::SwitchState::OFF); + heaterHandler.switchHeater(static_cast(idx), heater::SwitchState::OFF); } for (idx = 0; idx < thermalStates.size(); idx++) { thermalStates[idx].heating = false; @@ -1828,9 +1890,27 @@ void ThermalController::heaterSwitchHelperAllOff() { } } +ThermalController::~ThermalController() { + if (tmp1075SetPlPcdu1 != nullptr) { + delete tmp1075SetPlPcdu1; + } +} + +void ThermalController::crossCheckHeaterStateOfComponentsWhenHeaterGoesOff( + heater::Switch switchIdx) { + for (unsigned j = 0; j < thermalStates.size(); j++) { + if (thermalStates[j].heating and thermalStates[j].heaterSwitch == switchIdx) { + timeval currentTime; + Clock::getClock(¤tTime); + thermalStates[j].heating = false; + thermalStates[j].heaterEndTime = currentTime.tv_sec; + } + } +} + void ThermalController::tooHotHandlerWhichClearsOneShotFlag(object_id_t object, bool& oneShotFlag) { // Clear the one shot flag is the component is in acceptable temperature range. - if (not tooHotHandler(object, oneShotFlag) and not componentAboveUpperLimit) { + if (not tooHotHandler(object, oneShotFlag) and not ctrlCtx.componentAboveUpperLimit) { oneShotFlag = false; } } diff --git a/mission/controller/ThermalController.h b/mission/controller/ThermalController.h index 4eb41c67..1062fe97 100644 --- a/mission/controller/ThermalController.h +++ b/mission/controller/ThermalController.h @@ -1,7 +1,7 @@ #ifndef MISSION_CONTROLLER_THERMALCONTROLLER_H_ #define MISSION_CONTROLLER_THERMALCONTROLLER_H_ -#include +#include #include #include #include @@ -24,74 +24,7 @@ #include #include - -/** - * NOP Limit: Hard limit for device, usually from datasheet. Device damage is possible lif NOP limit - * is exceeded. - * OP Limit: Soft limit. Device should be switched off or TCS controller should take action if the - * limit is exceeded to avoid reaching NOP limit - */ -struct TempLimits { - TempLimits(float nopLowerLimit, float opLowerLimit, float cutOffLimit, float opUpperLimit, - float nopUpperLimit) - : opLowerLimit(opLowerLimit), - opUpperLimit(opUpperLimit), - cutOffLimit(cutOffLimit), - nopLowerLimit(nopLowerLimit), - nopUpperLimit(nopUpperLimit) {} - float opLowerLimit; - float opUpperLimit; - float cutOffLimit; - float nopLowerLimit; - float nopUpperLimit; -}; - -struct ThermalState { - uint8_t errorCounter; - // Is heating on for that thermal module? - bool heating = false; - heater::Switch heaterSwitch = heater::Switch::NUMBER_OF_SWITCHES; - // Heater start time and end times as UNIX seconds. Please note that these times will be updated - // when a switch command is sent, with no guarantess that the heater actually went on. - uint32_t heaterStartTime = 0; - uint32_t heaterEndTime = 0; -}; - -struct HeaterState { - bool switchTransition; - HeaterHandler::SwitchState target; - uint8_t heaterSwitchControlCycles; -}; - -using HeaterSwitchStates = std::array; - -enum ThermalComponents : uint8_t { - NONE = 0, - ACS_BOARD = 1, - MGT = 2, - RW = 3, - STR = 4, - IF_BOARD = 5, - TCS_BOARD = 6, - OBC = 7, - OBCIF_BOARD = 8, - SBAND_TRANSCEIVER = 9, - PCDUP60_BOARD = 10, - PCDUACU = 11, - PCDUPDU = 12, - PLPCDU_BOARD = 13, - PLOCMISSION_BOARD = 14, - PLOCPROCESSING_BOARD = 15, - DAC = 16, - CAMERA = 17, - DRO = 18, - X8 = 19, - HPA = 20, - TX = 21, - MPA = 22, - SCEX_BOARD = 23, - NUM_ENTRIES -}; +#include class ThermalController : public ExtendedControllerBase { public: @@ -102,8 +35,28 @@ class ThermalController : public ExtendedControllerBase { static constexpr int16_t SANITY_LIMIT_LOWER_TEMP = -80; static constexpr int16_t SANITY_LIMIT_UPPER_TEMP = 160; + // 1 hour + static constexpr uint32_t DEFAULT_MAX_HEATER_ON_DURATION_MS = 60 * 60 * 1000; + static constexpr uint32_t MAX_HEATER_ON_DURATIONS_MS[8] = {// PLOC PROC board + DEFAULT_MAX_HEATER_ON_DURATION_MS, + // PCDU PDU + DEFAULT_MAX_HEATER_ON_DURATION_MS, + // ACS Board + DEFAULT_MAX_HEATER_ON_DURATION_MS, + // OBC Board + DEFAULT_MAX_HEATER_ON_DURATION_MS, + // Camera + DEFAULT_MAX_HEATER_ON_DURATION_MS, + // STR + DEFAULT_MAX_HEATER_ON_DURATION_MS, + // DRO + DEFAULT_MAX_HEATER_ON_DURATION_MS, + // S-Band + DEFAULT_MAX_HEATER_ON_DURATION_MS}; + ThermalController(object_id_t objectId, HeaterHandler& heater, - const std::atomic_bool& tcsBoardShortUnavailable); + const std::atomic_bool& tcsBoardShortUnavailable, bool pollPcdu1Tmp); + virtual ~ThermalController(); ReturnValue_t initialize() override; @@ -111,16 +64,16 @@ class ThermalController : public ExtendedControllerBase { struct HeaterContext { public: HeaterContext(heater::Switch switchNr, heater::Switch redundantSwitchNr, - const TempLimits& tempLimit) + const tcsCtrl::TempLimits& tempLimit) : switchNr(switchNr), redSwitchNr(redundantSwitchNr), tempLimit(tempLimit) {} bool doHeaterHandling = true; heater::Switch switchNr; - HeaterHandler::SwitchState switchState = HeaterHandler::SwitchState::OFF; + heater::SwitchState switchState = heater::SwitchState::OFF; heater::Switch redSwitchNr; - const TempLimits& tempLimit; + const tcsCtrl::TempLimits& tempLimit; }; - void performThermalModuleCtrl(const HeaterSwitchStates& heaterSwitchStates); + void performThermalModuleCtrl(const tcsCtrl::HeaterSwitchStates& heaterSwitchStates); ReturnValue_t handleCommandMessage(CommandMessage* message) override; void performControlOperation() override; ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, @@ -142,12 +95,12 @@ class ThermalController : public ExtendedControllerBase { HeaterHandler& heaterHandler; + bool pollPcdu1Tmp; tcsCtrl::SensorTemperatures sensorTemperatures; tcsCtrl::SusTemperatures susTemperatures; tcsCtrl::DeviceTemperatures deviceTemperatures; tcsCtrl::HeaterInfo heaterInfo; - lp_vec_t currentVecPdu2 = - lp_vec_t(gp_id_t(objects::PDU2_HANDLER, PDU::pool::PDU_CURRENTS)); + tcsCtrl::TcsCtrlInfo tcsCtrlInfo; DeviceHandlerThermalSet imtqThermalSet; @@ -173,7 +126,7 @@ class ThermalController : public ExtendedControllerBase { TMP1075::Tmp1075Dataset tmp1075SetTcs1; TMP1075::Tmp1075Dataset tmp1075SetPlPcdu0; // damaged - // TMP1075::Tmp1075Dataset tmp1075SetPlPcdu1; + TMP1075::Tmp1075Dataset* tmp1075SetPlPcdu1; TMP1075::Tmp1075Dataset tmp1075SetIfBoard; // SUS @@ -227,57 +180,67 @@ class ThermalController : public ExtendedControllerBase { lp_var_t tempMgm2 = lp_var_t(objects::MGM_2_LIS3_HANDLER, mgmLis3::TEMPERATURE_CELCIUS); lp_var_t tempAdcPayloadPcdu = lp_var_t(objects::PLPCDU_HANDLER, plpcdu::TEMP); + lp_vec_t currentVecPdu2 = + lp_vec_t(gp_id_t(objects::PDU2_HANDLER, PDU::pool::PDU_CURRENTS)); // TempLimits - TempLimits acsBoardLimits = TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0); - TempLimits mgtLimits = TempLimits(-40.0, -40.0, 65.0, 70.0, 70.0); - TempLimits rwLimits = TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0); - TempLimits strLimits = TempLimits(-30.0, -20.0, 65.0, 70.0, 80.0); - TempLimits ifBoardLimits = TempLimits(-65.0, -40.0, 80.0, 85.0, 150.0); - TempLimits tcsBoardLimits = TempLimits(-60.0, -40.0, 80.0, 85.0, 130.0); - TempLimits obcLimits = TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0); - TempLimits obcIfBoardLimits = TempLimits(-65.0, -40.0, 80.0, 85.0, 125.0); - TempLimits sBandTransceiverLimits = TempLimits(-40.0, -25.0, 35.0, 40.0, 65.0); - TempLimits pcduP60BoardLimits = TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0); - TempLimits pcduAcuLimits = TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0); - TempLimits pcduPduLimits = TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0); - TempLimits plPcduBoardLimits = TempLimits(-55.0, -40.0, 80.0, 85.0, 125.0); - TempLimits plocMissionBoardLimits = TempLimits(-30.0, -10.0, 40.0, 45.0, 60); - TempLimits plocProcessingBoardLimits = TempLimits(-30.0, -10.0, 40.0, 45.0, 60.0); - TempLimits dacLimits = TempLimits(-65.0, -40.0, 113.0, 118.0, 150.0); - TempLimits cameraLimits = TempLimits(-40.0, -30.0, 60.0, 65.0, 85.0); - TempLimits droLimits = TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0); - TempLimits x8Limits = TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0); - TempLimits hpaLimits = TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0); - TempLimits txLimits = TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0); - TempLimits mpaLimits = TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0); - TempLimits scexBoardLimits = TempLimits(-60.0, -40.0, 80.0, 85.0, 150.0); + tcsCtrl::TempLimits acsBoardLimits = tcsCtrl::TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0); + tcsCtrl::TempLimits mgtLimits = tcsCtrl::TempLimits(-40.0, -40.0, 65.0, 70.0, 70.0); + tcsCtrl::TempLimits rwLimits = tcsCtrl::TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0); + tcsCtrl::TempLimits strLimits = tcsCtrl::TempLimits(-30.0, -20.0, 65.0, 70.0, 80.0); + tcsCtrl::TempLimits ifBoardLimits = tcsCtrl::TempLimits(-65.0, -40.0, 80.0, 85.0, 150.0); + tcsCtrl::TempLimits tcsBoardLimits = tcsCtrl::TempLimits(-60.0, -40.0, 80.0, 85.0, 130.0); + tcsCtrl::TempLimits obcLimits = tcsCtrl::TempLimits(-40.0, -40.0, 80.0, 85.0, 85.0); + tcsCtrl::TempLimits obcIfBoardLimits = tcsCtrl::TempLimits(-65.0, -40.0, 80.0, 85.0, 125.0); + tcsCtrl::TempLimits sBandTransceiverLimits = tcsCtrl::TempLimits(-40.0, -25.0, 35.0, 40.0, 65.0); + tcsCtrl::TempLimits pcduP60BoardLimits = tcsCtrl::TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0); + tcsCtrl::TempLimits pcduAcuLimits = tcsCtrl::TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0); + tcsCtrl::TempLimits pcduPduLimits = tcsCtrl::TempLimits(-35.0, -35.0, 80.0, 85.0, 85.0); + tcsCtrl::TempLimits plPcduBoardLimits = tcsCtrl::TempLimits(-55.0, -40.0, 80.0, 85.0, 125.0); + tcsCtrl::TempLimits plocMissionBoardLimits = tcsCtrl::TempLimits(-30.0, -10.0, 40.0, 45.0, 60); + tcsCtrl::TempLimits plocProcessingBoardLimits = + tcsCtrl::TempLimits(-30.0, -10.0, 40.0, 45.0, 60.0); + tcsCtrl::TempLimits dacLimits = tcsCtrl::TempLimits(-65.0, -40.0, 113.0, 118.0, 150.0); + tcsCtrl::TempLimits cameraLimits = tcsCtrl::TempLimits(-40.0, -30.0, 60.0, 65.0, 85.0); + tcsCtrl::TempLimits droLimits = tcsCtrl::TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0); + tcsCtrl::TempLimits x8Limits = tcsCtrl::TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0); + tcsCtrl::TempLimits hpaLimits = tcsCtrl::TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0); + tcsCtrl::TempLimits txLimits = tcsCtrl::TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0); + tcsCtrl::TempLimits mpaLimits = tcsCtrl::TempLimits(-40.0, -30.0, 75.0, 80.0, 90.0); + tcsCtrl::TempLimits scexBoardLimits = tcsCtrl::TempLimits(-60.0, -40.0, 80.0, 85.0, 150.0); + + struct CtrlContext { + double sensorTemp = INVALID_TEMPERATURE; + uint8_t currentSensorIndex = 0; + tcsCtrl::ThermalComponents thermalComponent = tcsCtrl::NONE; + bool redSwitchNrInUse = false; + bool componentAboveCutOffLimit = false; + bool componentAboveUpperLimit = false; + Event overHeatEventToTrigger; + } ctrlCtx; - double sensorTemp = INVALID_TEMPERATURE; - ThermalComponents thermalComponent = NONE; - bool redSwitchNrInUse = false; MessageQueueId_t camId = MessageQueueIF::NO_QUEUE; - bool componentAboveCutOffLimit = false; - bool componentAboveUpperLimit = false; - Event overHeatEventToTrigger; - bool eBandTooHotFlag = false; - bool camTooHotOneShotFlag = false; - bool scexTooHotFlag = false; - bool plocTooHotFlag = false; - bool pcduSystemTooHotFlag = false; - bool syrlinksTooHotFlag = false; - bool obcTooHotFlag = false; - bool mgtTooHotFlag = false; - bool strTooHotFlag = false; - bool rwTooHotFlag = false; + + struct TooHotFlags { + bool eBandTooHotFlag = false; + bool camTooHotOneShotFlag = false; + bool scexTooHotFlag = false; + bool plocTooHotFlag = false; + bool pcduSystemTooHotFlag = false; + bool syrlinksTooHotFlag = false; + bool obcTooHotFlag = false; + bool mgtTooHotFlag = false; + bool strTooHotFlag = false; + bool rwTooHotFlag = false; + } tooHotFlags; bool transitionWhenHeatersOff = false; uint32_t transitionWhenHeatersOffCycles = 0; Mode_t targetMode = MODE_OFF; Submode_t targetSubmode = SUBMODE_NONE; uint32_t cycles = 0; - std::array thermalStates{}; - std::array heaterStates{}; + std::array thermalStates{}; + std::array heaterStates{}; // Initial delay to make sure all pool variables have been initialized their owners. // Also, wait for system initialization to complete. @@ -298,6 +261,12 @@ class ThermalController : public ExtendedControllerBase { PoolEntry heaterSwitchStates = PoolEntry(heater::NUMBER_OF_SWITCHES); PoolEntry heaterCurrent = PoolEntry(); + PoolEntry tcsCtrlHeaterOn = PoolEntry(tcsCtrl::NUM_THERMAL_COMPONENTS); + PoolEntry tcsCtrlSensorIdx = PoolEntry(tcsCtrl::NUM_THERMAL_COMPONENTS); + PoolEntry tcsCtrlHeaterIdx = PoolEntry(tcsCtrl::NUM_THERMAL_COMPONENTS); + PoolEntry tcsCtrlStartTimes = PoolEntry(tcsCtrl::NUM_THERMAL_COMPONENTS); + PoolEntry tcsCtrlEndTimes = PoolEntry(tcsCtrl::NUM_THERMAL_COMPONENTS); + static constexpr dur_millis_t MUTEX_TIMEOUT = 50; void startTransition(Mode_t mode, Submode_t submode) override; @@ -319,8 +288,8 @@ class ThermalController : public ExtendedControllerBase { bool selectAndReadSensorTemp(HeaterContext& htrCtx); void heaterSwitchHelperAllOff(); - void heaterSwitchHelper(heater::Switch switchNr, HeaterHandler::SwitchState state, - unsigned componentIdx); + void heaterSwitchHelper(heater::Switch switchNr, heater::SwitchState state, + std::optional componentIdx); void ctrlAcsBoard(); void ctrlMgt(); @@ -329,7 +298,6 @@ class ThermalController : public ExtendedControllerBase { void ctrlIfBoard(); void ctrlTcsBoard(); void ctrlObc(); - void ctrlObcIfBoard(); void ctrlSBandTransceiver(); void ctrlPcduP60Board(); void ctrlPcduAcu(); @@ -345,7 +313,22 @@ class ThermalController : public ExtendedControllerBase { void ctrlTx(); void ctrlMpa(); void ctrlScexBoard(); - void heaterTransitionControl(const HeaterSwitchStates& currentHeaterStates); + + /** + * The transition of heaters might take some time. As long as a transition is + * going on, the TCS controller works in a reduced form. This function takes care + * of tracking transition and capturing their completion. + * @param currentHeaterStates + */ + void heaterTransitionControl(const tcsCtrl::HeaterSwitchStates& currentHeaterStates); + /** + * Control tasks to prevent heaters being on for prolonged periods. Ideally, this + * should never happen, but this task prevents bugs from causing heaters to stay on + * for a long time, which draws a lot of power. + * @param currentHeaterStates + */ + void heaterMaxDurationControl(const tcsCtrl::HeaterSwitchStates& currentHeaterStates); + void crossCheckHeaterStateOfComponentsWhenHeaterGoesOff(heater::Switch switchIdx); void setMode(Mode_t mode, Submode_t submode); uint32_t tempFloatToU32() const; bool tooHotHandler(object_id_t object, bool& oneShotFlag); diff --git a/mission/controller/acs/AcsParameters.cpp b/mission/controller/acs/AcsParameters.cpp index 16e46631..bcb1a7f4 100644 --- a/mission/controller/acs/AcsParameters.cpp +++ b/mission/controller/acs/AcsParameters.cpp @@ -26,6 +26,9 @@ ReturnValue_t AcsParameters::getParameter(uint8_t domainId, uint8_t parameterId, case 0x1: parameterWrapper->set(onBoardParams.mekfViolationTimer); break; + case 0x2: + parameterWrapper->set(onBoardParams.fusedRateSafeDuringEclipse); + break; default: return INVALID_IDENTIFIER_ID; } @@ -105,6 +108,9 @@ ReturnValue_t AcsParameters::getParameter(uint8_t domainId, uint8_t parameterId, parameterWrapper->setVector(mgmHandlingParameters.mgm4variance); break; case 0x12: + parameterWrapper->set(mgmHandlingParameters.mgmVectorFilterWeight); + break; + case 0x13: parameterWrapper->set(mgmHandlingParameters.mgmDerivativeFilterWeight); break; default: @@ -221,6 +227,15 @@ ReturnValue_t AcsParameters::getParameter(uint8_t domainId, uint8_t parameterId, case 0x23: parameterWrapper->setMatrix(susHandlingParameters.sus11coeffBeta); break; + case 0x24: + parameterWrapper->set(susHandlingParameters.susBrightnessThreshold); + break; + case 0x25: + parameterWrapper->set(susHandlingParameters.susVectorFilterWeight); + break; + case 0x26: + parameterWrapper->set(susHandlingParameters.susRateFilterWeight); + break; default: return INVALID_IDENTIFIER_ID; } @@ -336,26 +351,41 @@ ReturnValue_t AcsParameters::getParameter(uint8_t domainId, uint8_t parameterId, parameterWrapper->set(safeModeControllerParameters.k_parallelMekf); break; case 0x3: - parameterWrapper->set(safeModeControllerParameters.k_orthoNonMekf); + parameterWrapper->set(safeModeControllerParameters.k_orthoGyr); break; case 0x4: - parameterWrapper->set(safeModeControllerParameters.k_alignNonMekf); + parameterWrapper->set(safeModeControllerParameters.k_alignGyr); break; case 0x5: - parameterWrapper->set(safeModeControllerParameters.k_parallelNonMekf); + parameterWrapper->set(safeModeControllerParameters.k_parallelGyr); break; case 0x6: - parameterWrapper->setVector(safeModeControllerParameters.sunTargetDirLeop); + parameterWrapper->set(safeModeControllerParameters.k_orthoSusMgm); break; case 0x7: - parameterWrapper->setVector(safeModeControllerParameters.sunTargetDir); + parameterWrapper->set(safeModeControllerParameters.k_alignSusMgm); break; case 0x8: - parameterWrapper->set(safeModeControllerParameters.useMekf); + parameterWrapper->set(safeModeControllerParameters.k_parallelSusMgm); break; case 0x9: + parameterWrapper->setVector(safeModeControllerParameters.sunTargetDirLeop); + break; + case 0xA: + parameterWrapper->setVector(safeModeControllerParameters.sunTargetDir); + break; + case 0xB: + parameterWrapper->set(safeModeControllerParameters.useMekf); + break; + case 0xC: + parameterWrapper->set(safeModeControllerParameters.useGyr); + break; + case 0xD: parameterWrapper->set(safeModeControllerParameters.dampingDuringEclipse); break; + case 0xE: + parameterWrapper->set(safeModeControllerParameters.sineLimitSunRotRate); + break; default: return INVALID_IDENTIFIER_ID; } diff --git a/mission/controller/acs/AcsParameters.h b/mission/controller/acs/AcsParameters.h index 15725f2f..0f2b53c9 100644 --- a/mission/controller/acs/AcsParameters.h +++ b/mission/controller/acs/AcsParameters.h @@ -19,6 +19,7 @@ class AcsParameters : public HasParametersIF { struct OnBoardParams { double sampleTime = 0.4; // [s] uint16_t mekfViolationTimer = 750; + uint8_t fusedRateSafeDuringEclipse = true; } onBoardParams; struct InertiaEIVE { @@ -77,7 +78,8 @@ class AcsParameters : public HasParametersIF { float mgm02variance[3] = {pow(3.2e-7, 2), pow(3.2e-7, 2), pow(4.1e-7, 2)}; float mgm13variance[3] = {pow(1.5e-8, 2), pow(1.5e-8, 2), pow(1.5e-8, 2)}; float mgm4variance[3] = {pow(1.7e-6, 2), pow(1.7e-6, 2), pow(1.7e-6, 2)}; - float mgmDerivativeFilterWeight = 0.5; + float mgmVectorFilterWeight = 0.85; + float mgmDerivativeFilterWeight = 0.85; } mgmHandlingParameters; struct SusHandlingParameters { @@ -766,6 +768,9 @@ class AcsParameters : public HasParametersIF { {116.975421945286, -5.53022680362263, -5.61081660666997, 0.109754904982136, 0.167666815691513, 0.163137400730063, -0.000609874123906977, -0.00205336098697513, -0.000889232196185857, -0.00168429567131815}}; + float susBrightnessThreshold = 0.7; + float susVectorFilterWeight = .85; + float susRateFilterWeight = .85; } susHandlingParameters; struct GyrHandlingParameters { @@ -824,15 +829,22 @@ class AcsParameters : public HasParametersIF { double k_alignMekf = 4.0e-5; double k_parallelMekf = 3.75e-4; - double k_orthoNonMekf = 4.4e-3; - double k_alignNonMekf = 4.0e-5; - double k_parallelNonMekf = 3.75e-4; + double k_orthoGyr = 4.4e-3; + double k_alignGyr = 4.0e-5; + double k_parallelGyr = 3.75e-4; + + double k_orthoSusMgm = 1.1e-2; + double k_alignSusMgm = 2.0e-5; + double k_parallelSusMgm = 4.4e-4; double sunTargetDirLeop[3] = {0, sqrt(.5), sqrt(.5)}; double sunTargetDir[3] = {0, 0, 1}; uint8_t useMekf = false; + uint8_t useGyr = true; uint8_t dampingDuringEclipse = true; + + float sineLimitSunRotRate = 0.24; } safeModeControllerParameters; struct PointingLawParameters { diff --git a/mission/controller/acs/CMakeLists.txt b/mission/controller/acs/CMakeLists.txt index 3c4a3475..a8b0e9a6 100644 --- a/mission/controller/acs/CMakeLists.txt +++ b/mission/controller/acs/CMakeLists.txt @@ -2,6 +2,7 @@ target_sources( ${LIB_EIVE_MISSION} PRIVATE AcsParameters.cpp ActuatorCmd.cpp + FusedRotationEstimation.cpp Guidance.cpp Igrf13Model.cpp MultiplicativeKalmanFilter.cpp diff --git a/mission/controller/acs/FusedRotationEstimation.cpp b/mission/controller/acs/FusedRotationEstimation.cpp new file mode 100644 index 00000000..4f1dad45 --- /dev/null +++ b/mission/controller/acs/FusedRotationEstimation.cpp @@ -0,0 +1,116 @@ +#include "FusedRotationEstimation.h" + +FusedRotationEstimation::FusedRotationEstimation(AcsParameters *acsParameters_) { + acsParameters = acsParameters_; +} + +void FusedRotationEstimation::estimateFusedRotationRateSafe( + acsctrl::SusDataProcessed *susDataProcessed, acsctrl::MgmDataProcessed *mgmDataProcessed, + acsctrl::GyrDataProcessed *gyrDataProcessed, acsctrl::FusedRotRateData *fusedRotRateData) { + if ((not mgmDataProcessed->mgmVecTot.isValid() and not susDataProcessed->susVecTot.isValid() and + not fusedRotRateData->rotRateTotal.isValid()) or + (not susDataProcessed->susVecTotDerivative.isValid() and + not mgmDataProcessed->mgmVecTotDerivative.isValid())) { + { + PoolReadGuard pg(fusedRotRateData); + std::memcpy(fusedRotRateData->rotRateOrthogonal.value, ZERO_VEC, 3 * sizeof(double)); + std::memcpy(fusedRotRateData->rotRateParallel.value, ZERO_VEC, 3 * sizeof(double)); + std::memcpy(fusedRotRateData->rotRateTotal.value, ZERO_VEC, 3 * sizeof(double)); + fusedRotRateData->setValidity(false, true); + } + // store for calculation of angular acceleration + if (gyrDataProcessed->gyrVecTot.isValid()) { + std::memcpy(rotRateOldB, gyrDataProcessed->gyrVecTot.value, 3 * sizeof(double)); + } + return; + } + if (not susDataProcessed->susVecTot.isValid()) { + estimateFusedRotationRateEclipse(gyrDataProcessed, fusedRotRateData); + // store for calculation of angular acceleration + if (gyrDataProcessed->gyrVecTot.isValid()) { + std::memcpy(rotRateOldB, gyrDataProcessed->gyrVecTot.value, 3 * sizeof(double)); + } + return; + } + + // calculate rotation around the sun + double magSunCross[3] = {0, 0, 0}; + + VectorOperations::cross(mgmDataProcessed->mgmVecTot.value, + susDataProcessed->susVecTot.value, magSunCross); + double magSunCrossNorm = VectorOperations::norm(magSunCross, 3); + double magNorm = VectorOperations::norm(mgmDataProcessed->mgmVecTot.value, 3); + double fusedRotRateParallel[3] = {0, 0, 0}; + if (magSunCrossNorm > + (acsParameters->safeModeControllerParameters.sineLimitSunRotRate * magNorm)) { + double omegaParallel = + VectorOperations::dot(mgmDataProcessed->mgmVecTotDerivative.value, magSunCross) * + pow(magSunCrossNorm, -2); + VectorOperations::mulScalar(susDataProcessed->susVecTot.value, omegaParallel, + fusedRotRateParallel, 3); + } else { + estimateFusedRotationRateEclipse(gyrDataProcessed, fusedRotRateData); + // store for calculation of angular acceleration + if (gyrDataProcessed->gyrVecTot.isValid()) { + std::memcpy(rotRateOldB, gyrDataProcessed->gyrVecTot.value, 3 * sizeof(double)); + } + return; + } + + // calculate rotation orthogonal to the sun + double fusedRotRateOrthogonal[3] = {0, 0, 0}; + VectorOperations::cross(susDataProcessed->susVecTotDerivative.value, + susDataProcessed->susVecTot.value, fusedRotRateOrthogonal); + VectorOperations::mulScalar( + fusedRotRateOrthogonal, + pow(VectorOperations::norm(susDataProcessed->susVecTot.value, 3), -2), + fusedRotRateOrthogonal, 3); + + // calculate total rotation rate + double fusedRotRateTotal[3] = {0, 0, 0}; + VectorOperations::add(fusedRotRateParallel, fusedRotRateOrthogonal, fusedRotRateTotal); + + { + PoolReadGuard pg(fusedRotRateData); + std::memcpy(fusedRotRateData->rotRateOrthogonal.value, fusedRotRateOrthogonal, + 3 * sizeof(double)); + std::memcpy(fusedRotRateData->rotRateParallel.value, fusedRotRateParallel, 3 * sizeof(double)); + std::memcpy(fusedRotRateData->rotRateTotal.value, fusedRotRateTotal, 3 * sizeof(double)); + fusedRotRateData->setValidity(true, true); + } + + // store for calculation of angular acceleration + if (gyrDataProcessed->gyrVecTot.isValid()) { + std::memcpy(rotRateOldB, gyrDataProcessed->gyrVecTot.value, 3 * sizeof(double)); + } +} + +void FusedRotationEstimation::estimateFusedRotationRateEclipse( + acsctrl::GyrDataProcessed *gyrDataProcessed, acsctrl::FusedRotRateData *fusedRotRateData) { + if (not acsParameters->onBoardParams.fusedRateSafeDuringEclipse or + not gyrDataProcessed->gyrVecTot.isValid() or + VectorOperations::norm(fusedRotRateData->rotRateTotal.value, 3) == 0) { + { + PoolReadGuard pg(fusedRotRateData); + std::memcpy(fusedRotRateData->rotRateOrthogonal.value, ZERO_VEC, 3 * sizeof(double)); + std::memcpy(fusedRotRateData->rotRateParallel.value, ZERO_VEC, 3 * sizeof(double)); + std::memcpy(fusedRotRateData->rotRateTotal.value, ZERO_VEC, 3 * sizeof(double)); + fusedRotRateData->setValidity(false, true); + } + return; + } + double angAccelB[3] = {0, 0, 0}; + VectorOperations::subtract(gyrDataProcessed->gyrVecTot.value, rotRateOldB, angAccelB, 3); + double fusedRotRateTotal[3] = {0, 0, 0}; + VectorOperations::add(fusedRotRateData->rotRateTotal.value, angAccelB, fusedRotRateTotal, + 3); + { + PoolReadGuard pg(fusedRotRateData); + std::memcpy(fusedRotRateData->rotRateOrthogonal.value, ZERO_VEC, 3 * sizeof(double)); + fusedRotRateData->rotRateOrthogonal.setValid(false); + std::memcpy(fusedRotRateData->rotRateParallel.value, ZERO_VEC, 3 * sizeof(double)); + fusedRotRateData->rotRateParallel.setValid(false); + std::memcpy(fusedRotRateData->rotRateTotal.value, fusedRotRateTotal, 3 * sizeof(double)); + fusedRotRateData->rotRateTotal.setValid(true); + } +} diff --git a/mission/controller/acs/FusedRotationEstimation.h b/mission/controller/acs/FusedRotationEstimation.h new file mode 100644 index 00000000..fa4fda1c --- /dev/null +++ b/mission/controller/acs/FusedRotationEstimation.h @@ -0,0 +1,29 @@ +#ifndef MISSION_CONTROLLER_ACS_FUSEDROTATIONESTIMATION_H_ +#define MISSION_CONTROLLER_ACS_FUSEDROTATIONESTIMATION_H_ + +#include +#include +#include +#include + +class FusedRotationEstimation { + public: + FusedRotationEstimation(AcsParameters *acsParameters_); + + void estimateFusedRotationRateSafe(acsctrl::SusDataProcessed *susDataProcessed, + acsctrl::MgmDataProcessed *mgmDataProcessed, + acsctrl::GyrDataProcessed *gyrDataProcessed, + acsctrl::FusedRotRateData *fusedRotRateData); + + protected: + private: + static constexpr double ZERO_VEC[3] = {0, 0, 0}; + + AcsParameters *acsParameters; + double rotRateOldB[3] = {0, 0, 0}; + + void estimateFusedRotationRateEclipse(acsctrl::GyrDataProcessed *gyrDataProcessed, + acsctrl::FusedRotRateData *fusedRotRateData); +}; + +#endif /* MISSION_CONTROLLER_ACS_FUSEDROTATIONESTIMATION_H_ */ diff --git a/mission/controller/acs/SensorProcessing.cpp b/mission/controller/acs/SensorProcessing.cpp index b106baaa..511cae35 100644 --- a/mission/controller/acs/SensorProcessing.cpp +++ b/mission/controller/acs/SensorProcessing.cpp @@ -30,10 +30,7 @@ void SensorProcessing::processMgm(const float *mgm0Value, bool mgm0valid, const // ------------------------------------------------ double magIgrfModel[3] = {0.0, 0.0, 0.0}; if (gpsValid) { - // Should be existing class object which will be called and modified here. Igrf13Model igrf13; - // So the line above should not be done here. Update: Can be done here as long updated coffs - // stored in acsParameters ? igrf13.schmidtNormalization(); igrf13.updateCoeffGH(timeOfMgmMeasurement); // maybe put a condition here, to only update after a full day, this @@ -45,14 +42,13 @@ void SensorProcessing::processMgm(const float *mgm0Value, bool mgm0valid, const { PoolReadGuard pg(mgmDataProcessed); if (pg.getReadResult() == returnvalue::OK) { - float zeroVec[3] = {0.0, 0.0, 0.0}; - std::memcpy(mgmDataProcessed->mgm0vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(mgmDataProcessed->mgm1vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(mgmDataProcessed->mgm2vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(mgmDataProcessed->mgm3vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(mgmDataProcessed->mgm4vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(mgmDataProcessed->mgmVecTot.value, zeroVec, 3 * sizeof(float)); - std::memcpy(mgmDataProcessed->mgmVecTotDerivative.value, zeroVec, 3 * sizeof(float)); + std::memcpy(mgmDataProcessed->mgm0vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(mgmDataProcessed->mgm1vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(mgmDataProcessed->mgm2vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(mgmDataProcessed->mgm3vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(mgmDataProcessed->mgm4vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(mgmDataProcessed->mgmVecTot.value, ZERO_VEC_D, 3 * sizeof(double)); + std::memcpy(mgmDataProcessed->mgmVecTotDerivative.value, ZERO_VEC_D, 3 * sizeof(double)); mgmDataProcessed->setValidity(false, true); std::memcpy(mgmDataProcessed->magIgrfModel.value, magIgrfModel, 3 * sizeof(double)); mgmDataProcessed->magIgrfModel.setValid(gpsValid); @@ -136,6 +132,10 @@ void SensorProcessing::processMgm(const float *mgm0Value, bool mgm0valid, const for (uint8_t i = 0; i < 3; i++) { mgmVecTot[i] = sensorFusionNumerator[i] / sensorFusionDenominator[i]; } + if (VectorOperations::norm(mgmVecTot, 3) != 0 and mgmDataProcessed->mgmVecTot.isValid()) { + lowPassFilter(mgmVecTot, mgmDataProcessed->mgmVecTot.value, + mgmParameters->mgmVectorFilterWeight); + } //-----------------------Mgm Rate Computation --------------------------------------------------- double mgmVecTotDerivative[3] = {0.0, 0.0, 0.0}; @@ -210,63 +210,68 @@ void SensorProcessing::processSus( sunIjkModel[0] = cos(eclipticLongitude); sunIjkModel[1] = sin(eclipticLongitude) * cos(epsilon); sunIjkModel[2] = sin(eclipticLongitude) * sin(epsilon); + + uint64_t susBrightness[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; if (sus0valid) { - sus0valid = susConverter.checkSunSensorData(sus0Value); + susBrightness[0] = susConverter.checkSunSensorData(sus0Value); } if (sus1valid) { - sus1valid = susConverter.checkSunSensorData(sus1Value); + susBrightness[1] = susConverter.checkSunSensorData(sus1Value); } if (sus2valid) { - sus2valid = susConverter.checkSunSensorData(sus2Value); + susBrightness[2] = susConverter.checkSunSensorData(sus2Value); } if (sus3valid) { - sus3valid = susConverter.checkSunSensorData(sus3Value); + susBrightness[3] = susConverter.checkSunSensorData(sus3Value); } if (sus4valid) { - sus4valid = susConverter.checkSunSensorData(sus4Value); + susBrightness[4] = susConverter.checkSunSensorData(sus4Value); } if (sus5valid) { - sus5valid = susConverter.checkSunSensorData(sus5Value); + susBrightness[5] = susConverter.checkSunSensorData(sus5Value); } if (sus6valid) { - sus6valid = susConverter.checkSunSensorData(sus6Value); + susBrightness[6] = susConverter.checkSunSensorData(sus6Value); } if (sus7valid) { - sus7valid = susConverter.checkSunSensorData(sus7Value); + susBrightness[7] = susConverter.checkSunSensorData(sus7Value); } if (sus8valid) { - sus8valid = susConverter.checkSunSensorData(sus8Value); + susBrightness[8] = susConverter.checkSunSensorData(sus8Value); } if (sus9valid) { - sus9valid = susConverter.checkSunSensorData(sus9Value); + susBrightness[9] = susConverter.checkSunSensorData(sus9Value); } if (sus10valid) { - sus10valid = susConverter.checkSunSensorData(sus10Value); + susBrightness[10] = susConverter.checkSunSensorData(sus10Value); } if (sus11valid) { - sus11valid = susConverter.checkSunSensorData(sus11Value); + susBrightness[11] = susConverter.checkSunSensorData(sus11Value); } - if (!sus0valid && !sus1valid && !sus2valid && !sus3valid && !sus4valid && !sus5valid && - !sus6valid && !sus7valid && !sus8valid && !sus9valid && !sus10valid && !sus11valid) { + bool susValid[12] = {sus0valid, sus1valid, sus2valid, sus3valid, sus4valid, sus5valid, + sus6valid, sus7valid, sus8valid, sus9valid, sus10valid, sus11valid}; + bool allInvalid = + susConverter.checkValidity(susValid, susBrightness, susParameters->susBrightnessThreshold); + + if (allInvalid) { { PoolReadGuard pg(susDataProcessed); if (pg.getReadResult() == returnvalue::OK) { - float zeroVec[3] = {0.0, 0.0, 0.0}; - std::memcpy(susDataProcessed->sus0vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->sus1vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->sus2vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->sus3vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->sus4vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->sus5vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->sus6vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->sus7vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->sus8vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->sus9vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->sus10vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->sus11vec.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->susVecTot.value, zeroVec, 3 * sizeof(float)); - std::memcpy(susDataProcessed->susVecTotDerivative.value, zeroVec, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus0vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus1vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus2vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus3vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus4vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus5vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus6vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus7vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus8vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus9vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus10vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus11vec.value, ZERO_VEC_F, 3 * sizeof(float)); + std::memcpy(susDataProcessed->susVecTot.value, ZERO_VEC_D, 3 * sizeof(double)); + std::memcpy(susDataProcessed->susVecTotDerivative.value, ZERO_VEC_D, 3 * sizeof(double)); susDataProcessed->setValidity(false, true); std::memcpy(susDataProcessed->sunIjkModel.value, sunIjkModel, 3 * sizeof(double)); susDataProcessed->sunIjkModel.setValid(true); @@ -274,122 +279,87 @@ void SensorProcessing::processSus( } return; } - // WARNING: NOT TRANSFORMED IN BODY FRAME YET - // Transformation into Geomtry Frame - float sus0VecBody[3] = {0, 0, 0}, sus1VecBody[3] = {0, 0, 0}, sus2VecBody[3] = {0, 0, 0}, - sus3VecBody[3] = {0, 0, 0}, sus4VecBody[3] = {0, 0, 0}, sus5VecBody[3] = {0, 0, 0}, - sus6VecBody[3] = {0, 0, 0}, sus7VecBody[3] = {0, 0, 0}, sus8VecBody[3] = {0, 0, 0}, - sus9VecBody[3] = {0, 0, 0}, sus10VecBody[3] = {0, 0, 0}, sus11VecBody[3] = {0, 0, 0}; - if (sus0valid) { - MatrixOperations::multiply( - susParameters->sus0orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus0Value, susParameters->sus0coeffAlpha, - susParameters->sus0coeffBeta), - sus0VecBody, 3, 3, 1); - } - if (sus1valid) { - MatrixOperations::multiply( - susParameters->sus1orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus1Value, susParameters->sus1coeffAlpha, - susParameters->sus1coeffBeta), - sus1VecBody, 3, 3, 1); - } - if (sus2valid) { - MatrixOperations::multiply( - susParameters->sus2orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus2Value, susParameters->sus2coeffAlpha, - susParameters->sus2coeffBeta), - sus2VecBody, 3, 3, 1); - } - if (sus3valid) { - MatrixOperations::multiply( - susParameters->sus3orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus3Value, susParameters->sus3coeffAlpha, - susParameters->sus3coeffBeta), - sus3VecBody, 3, 3, 1); - } - if (sus4valid) { - MatrixOperations::multiply( - susParameters->sus4orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus4Value, susParameters->sus4coeffAlpha, - susParameters->sus4coeffBeta), - sus4VecBody, 3, 3, 1); - } - if (sus5valid) { - MatrixOperations::multiply( - susParameters->sus5orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus5Value, susParameters->sus5coeffAlpha, - susParameters->sus5coeffBeta), - sus5VecBody, 3, 3, 1); - } - if (sus6valid) { - MatrixOperations::multiply( - susParameters->sus6orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus6Value, susParameters->sus6coeffAlpha, - susParameters->sus6coeffBeta), - sus6VecBody, 3, 3, 1); - } - if (sus7valid) { - MatrixOperations::multiply( - susParameters->sus7orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus7Value, susParameters->sus7coeffAlpha, - susParameters->sus7coeffBeta), - sus7VecBody, 3, 3, 1); - } - if (sus8valid) { - MatrixOperations::multiply( - susParameters->sus8orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus8Value, susParameters->sus8coeffAlpha, - susParameters->sus8coeffBeta), - sus8VecBody, 3, 3, 1); - } - if (sus9valid) { - MatrixOperations::multiply( - susParameters->sus9orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus9Value, susParameters->sus9coeffAlpha, - susParameters->sus9coeffBeta), - sus9VecBody, 3, 3, 1); - } - if (sus10valid) { - MatrixOperations::multiply( - susParameters->sus10orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus10Value, susParameters->sus10coeffAlpha, - susParameters->sus10coeffBeta), - sus10VecBody, 3, 3, 1); - } - if (sus11valid) { - MatrixOperations::multiply( - susParameters->sus11orientationMatrix[0], - susConverter.getSunVectorSensorFrame(sus11Value, susParameters->sus11coeffAlpha, - susParameters->sus11coeffBeta), - sus11VecBody, 3, 3, 1); - } + float susVecSensor[12][3] = {{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}}; + float susVecBody[12][3] = {{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}}; - /* ------ Mean Value: susDirEst ------ */ - bool validIds[12] = {sus0valid, sus1valid, sus2valid, sus3valid, sus4valid, sus5valid, - sus6valid, sus7valid, sus8valid, sus9valid, sus10valid, sus11valid}; - float susVecBody[3][12] = {{sus0VecBody[0], sus1VecBody[0], sus2VecBody[0], sus3VecBody[0], - sus4VecBody[0], sus5VecBody[0], sus6VecBody[0], sus7VecBody[0], - sus8VecBody[0], sus9VecBody[0], sus10VecBody[0], sus11VecBody[0]}, - {sus0VecBody[1], sus1VecBody[1], sus2VecBody[1], sus3VecBody[1], - sus4VecBody[1], sus5VecBody[1], sus6VecBody[1], sus7VecBody[1], - sus8VecBody[1], sus9VecBody[1], sus10VecBody[1], sus11VecBody[1]}, - {sus0VecBody[2], sus1VecBody[2], sus2VecBody[2], sus3VecBody[2], - sus4VecBody[2], sus5VecBody[2], sus6VecBody[2], sus7VecBody[2], - sus8VecBody[2], sus9VecBody[2], sus10VecBody[2], sus11VecBody[2]}}; + if (susValid[0]) { + susConverter.calculateSunVector(susVecSensor[0], sus0Value); + MatrixOperations::multiply(susParameters->sus0orientationMatrix[0], susVecSensor[0], + susVecBody[0], 3, 3, 1); + } + if (susValid[1]) { + susConverter.calculateSunVector(susVecSensor[1], sus1Value); + MatrixOperations::multiply(susParameters->sus1orientationMatrix[0], susVecSensor[1], + susVecBody[1], 3, 3, 1); + } + if (susValid[2]) { + susConverter.calculateSunVector(susVecSensor[2], sus2Value); + MatrixOperations::multiply(susParameters->sus2orientationMatrix[0], susVecSensor[2], + susVecBody[2], 3, 3, 1); + } + if (susValid[3]) { + susConverter.calculateSunVector(susVecSensor[3], sus3Value); + MatrixOperations::multiply(susParameters->sus3orientationMatrix[0], susVecSensor[3], + susVecBody[3], 3, 3, 1); + } + if (susValid[4]) { + susConverter.calculateSunVector(susVecSensor[4], sus4Value); + MatrixOperations::multiply(susParameters->sus4orientationMatrix[0], susVecSensor[4], + susVecBody[4], 3, 3, 1); + } + if (susValid[5]) { + susConverter.calculateSunVector(susVecSensor[5], sus5Value); + MatrixOperations::multiply(susParameters->sus5orientationMatrix[0], susVecSensor[5], + susVecBody[5], 3, 3, 1); + } + if (susValid[6]) { + susConverter.calculateSunVector(susVecSensor[6], sus6Value); + MatrixOperations::multiply(susParameters->sus6orientationMatrix[0], susVecSensor[6], + susVecBody[6], 3, 3, 1); + } + if (susValid[7]) { + susConverter.calculateSunVector(susVecSensor[7], sus7Value); + MatrixOperations::multiply(susParameters->sus7orientationMatrix[0], susVecSensor[7], + susVecBody[7], 3, 3, 1); + } + if (susValid[8]) { + susConverter.calculateSunVector(susVecSensor[8], sus8Value); + MatrixOperations::multiply(susParameters->sus8orientationMatrix[0], susVecSensor[8], + susVecBody[8], 3, 3, 1); + } + if (susValid[9]) { + susConverter.calculateSunVector(susVecSensor[9], sus9Value); + MatrixOperations::multiply(susParameters->sus9orientationMatrix[0], susVecSensor[9], + susVecBody[9], 3, 3, 1); + } + if (susValid[10]) { + susConverter.calculateSunVector(susVecSensor[10], sus10Value); + MatrixOperations::multiply(susParameters->sus10orientationMatrix[0], susVecSensor[10], + susVecBody[10], 3, 3, 1); + } + if (susValid[11]) { + susConverter.calculateSunVector(susVecSensor[11], sus11Value); + MatrixOperations::multiply(susParameters->sus11orientationMatrix[0], susVecSensor[11], + susVecBody[11], 3, 3, 1); + } double susMeanValue[3] = {0, 0, 0}; for (uint8_t i = 0; i < 12; i++) { - if (validIds[i]) { - susMeanValue[0] += susVecBody[0][i]; - susMeanValue[1] += susVecBody[1][i]; - susMeanValue[2] += susVecBody[2][i]; - } + susMeanValue[0] += susVecBody[i][0]; + susMeanValue[1] += susVecBody[i][1]; + susMeanValue[2] += susVecBody[i][2]; } double susVecTot[3] = {0.0, 0.0, 0.0}; VectorOperations::normalize(susMeanValue, susVecTot, 3); + if (VectorOperations::norm(susVecTot, 3) != 0 and susDataProcessed->susVecTot.isValid()) { + lowPassFilter(susVecTot, susDataProcessed->susVecTot.value, + susParameters->susVectorFilterWeight); + } + /* -------- Sun Derivatiative --------------------- */ double susVecTotDerivative[3] = {0.0, 0.0, 0.0}; @@ -402,33 +372,38 @@ void SensorProcessing::processSus( susVecTotDerivativeValid = true; } } + if (VectorOperations::norm(susVecTotDerivative, 3) != 0 and + susDataProcessed->susVecTotDerivative.isValid()) { + lowPassFilter(susVecTotDerivative, susDataProcessed->susVecTotDerivative.value, + susParameters->susRateFilterWeight); + } timeOfSavedSusDirEst = timeOfSusMeasurement; { PoolReadGuard pg(susDataProcessed); if (pg.getReadResult() == returnvalue::OK) { - std::memcpy(susDataProcessed->sus0vec.value, sus0VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus0vec.value, susVecBody[0], 3 * sizeof(float)); susDataProcessed->sus0vec.setValid(sus0valid); - std::memcpy(susDataProcessed->sus1vec.value, sus1VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus1vec.value, susVecBody[1], 3 * sizeof(float)); susDataProcessed->sus1vec.setValid(sus1valid); - std::memcpy(susDataProcessed->sus2vec.value, sus2VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus2vec.value, susVecBody[2], 3 * sizeof(float)); susDataProcessed->sus2vec.setValid(sus2valid); - std::memcpy(susDataProcessed->sus3vec.value, sus3VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus3vec.value, susVecBody[3], 3 * sizeof(float)); susDataProcessed->sus3vec.setValid(sus3valid); - std::memcpy(susDataProcessed->sus4vec.value, sus4VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus4vec.value, susVecBody[4], 3 * sizeof(float)); susDataProcessed->sus4vec.setValid(sus4valid); - std::memcpy(susDataProcessed->sus5vec.value, sus5VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus5vec.value, susVecBody[5], 3 * sizeof(float)); susDataProcessed->sus5vec.setValid(sus5valid); - std::memcpy(susDataProcessed->sus6vec.value, sus6VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus6vec.value, susVecBody[6], 3 * sizeof(float)); susDataProcessed->sus6vec.setValid(sus6valid); - std::memcpy(susDataProcessed->sus7vec.value, sus7VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus7vec.value, susVecBody[7], 3 * sizeof(float)); susDataProcessed->sus7vec.setValid(sus7valid); - std::memcpy(susDataProcessed->sus8vec.value, sus8VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus8vec.value, susVecBody[8], 3 * sizeof(float)); susDataProcessed->sus8vec.setValid(sus8valid); - std::memcpy(susDataProcessed->sus9vec.value, sus9VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus9vec.value, susVecBody[9], 3 * sizeof(float)); susDataProcessed->sus9vec.setValid(sus9valid); - std::memcpy(susDataProcessed->sus10vec.value, sus10VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus10vec.value, susVecBody[10], 3 * sizeof(float)); susDataProcessed->sus10vec.setValid(sus10valid); - std::memcpy(susDataProcessed->sus11vec.value, sus11VecBody, 3 * sizeof(float)); + std::memcpy(susDataProcessed->sus11vec.value, susVecBody[11], 3 * sizeof(float)); susDataProcessed->sus11vec.setValid(sus11valid); std::memcpy(susDataProcessed->susVecTot.value, susVecTot, 3 * sizeof(double)); susDataProcessed->susVecTot.setValid(true); @@ -459,12 +434,11 @@ void SensorProcessing::processGyr( { PoolReadGuard pg(gyrDataProcessed); if (pg.getReadResult() == returnvalue::OK) { - double zeroVector[3] = {0.0, 0.0, 0.0}; - std::memcpy(gyrDataProcessed->gyr0vec.value, zeroVector, 3 * sizeof(double)); - std::memcpy(gyrDataProcessed->gyr1vec.value, zeroVector, 3 * sizeof(double)); - std::memcpy(gyrDataProcessed->gyr2vec.value, zeroVector, 3 * sizeof(double)); - std::memcpy(gyrDataProcessed->gyr3vec.value, zeroVector, 3 * sizeof(double)); - std::memcpy(gyrDataProcessed->gyrVecTot.value, zeroVector, 3 * sizeof(double)); + std::memcpy(gyrDataProcessed->gyr0vec.value, ZERO_VEC_D, 3 * sizeof(double)); + std::memcpy(gyrDataProcessed->gyr1vec.value, ZERO_VEC_D, 3 * sizeof(double)); + std::memcpy(gyrDataProcessed->gyr2vec.value, ZERO_VEC_D, 3 * sizeof(double)); + std::memcpy(gyrDataProcessed->gyr3vec.value, ZERO_VEC_D, 3 * sizeof(double)); + std::memcpy(gyrDataProcessed->gyrVecTot.value, ZERO_VEC_D, 3 * sizeof(double)); gyrDataProcessed->setValidity(false, true); } } diff --git a/mission/controller/acs/SensorProcessing.h b/mission/controller/acs/SensorProcessing.h index 47449a75..6dbc5d58 100644 --- a/mission/controller/acs/SensorProcessing.h +++ b/mission/controller/acs/SensorProcessing.h @@ -23,6 +23,9 @@ class SensorProcessing { acsctrl::GpsDataProcessed *gpsDataProcessed, const AcsParameters *acsParameters); // Will call protected functions private: + static constexpr float ZERO_VEC_F[3] = {0, 0, 0}; + static constexpr double ZERO_VEC_D[3] = {0, 0, 0}; + protected: // short description needed for every function void processMgm(const float *mgm0Value, bool mgm0valid, const float *mgm1Value, bool mgm1valid, diff --git a/mission/controller/acs/SusConverter.cpp b/mission/controller/acs/SusConverter.cpp index 1a645270..31cc0371 100644 --- a/mission/controller/acs/SusConverter.cpp +++ b/mission/controller/acs/SusConverter.cpp @@ -1,121 +1,64 @@ #include "SusConverter.h" -#include -#include -#include #include -#include - -bool SusConverter::checkSunSensorData(const uint16_t susChannel[6]) { - if (susChannel[0] <= susChannelValueCheckLow || susChannel[0] > susChannelValueCheckHigh || +uint64_t SusConverter::checkSunSensorData(const uint16_t susChannel[6]) { + if (susChannel[0] <= SUS_CHANNEL_VALUE_LOW || susChannel[0] > SUS_CHANNEL_VALUE_HIGH || susChannel[0] > susChannel[GNDREF]) { - return false; + return 0; } - if (susChannel[1] <= susChannelValueCheckLow || susChannel[1] > susChannelValueCheckHigh || + if (susChannel[1] <= SUS_CHANNEL_VALUE_LOW || susChannel[1] > SUS_CHANNEL_VALUE_HIGH || susChannel[1] > susChannel[GNDREF]) { - return false; + return 0; }; - if (susChannel[2] <= susChannelValueCheckLow || susChannel[2] > susChannelValueCheckHigh || + if (susChannel[2] <= SUS_CHANNEL_VALUE_LOW || susChannel[2] > SUS_CHANNEL_VALUE_HIGH || susChannel[2] > susChannel[GNDREF]) { - return false; + return 0; }; - if (susChannel[3] <= susChannelValueCheckLow || susChannel[3] > susChannelValueCheckHigh || + if (susChannel[3] <= SUS_CHANNEL_VALUE_LOW || susChannel[3] > SUS_CHANNEL_VALUE_HIGH || susChannel[3] > susChannel[GNDREF]) { - return false; + return 0; }; - susChannelValueSum = + uint64_t susChannelValueSum = 4 * susChannel[GNDREF] - (susChannel[0] + susChannel[1] + susChannel[2] + susChannel[3]); - if ((susChannelValueSum < susChannelValueSumHigh) && - (susChannelValueSum > susChannelValueSumLow)) { - return false; + if (susChannelValueSum < SUS_ALBEDO_CHECK) { + return 0; }; - return true; + return susChannelValueSum; } -void SusConverter::calcAngle(const uint16_t susChannel[6]) { - float xout, yout; - float s = 0.03; // s=[mm] gap between diodes - uint8_t d = 5; // d=[mm] edge length of the quadratic aperture - uint8_t h = 1; // h=[mm] distance between diodes and aperture - int ch0, ch1, ch2, ch3; +bool SusConverter::checkValidity(bool* susValid, const uint64_t brightness[12], + const float threshold) { + uint8_t maxBrightness = 0; + VectorOperations::maxValue(brightness, 12, &maxBrightness); + if (brightness[maxBrightness] == 0) { + return true; + } + for (uint8_t idx = 0; idx < 12; idx++) { + if ((idx != maxBrightness) and (brightness[idx] < threshold * brightness[maxBrightness])) { + susValid[idx] = false; + continue; + } + susValid[idx] = true; + } + return false; +} + +void SusConverter::calculateSunVector(float* sunVectorSensorFrame, const uint16_t susChannel[6]) { // Substract measurement values from GNDREF zero current threshold - ch0 = susChannel[GNDREF] - susChannel[0]; - ch1 = susChannel[GNDREF] - susChannel[1]; - ch2 = susChannel[GNDREF] - susChannel[2]; - ch3 = susChannel[GNDREF] - susChannel[3]; + float ch0 = susChannel[GNDREF] - susChannel[0]; + float ch1 = susChannel[GNDREF] - susChannel[1]; + float ch2 = susChannel[GNDREF] - susChannel[2]; + float ch3 = susChannel[GNDREF] - susChannel[3]; // Calculation of x and y - xout = ((d - s) / 2) * (ch2 - ch3 - ch0 + ch1) / (ch0 + ch1 + ch2 + ch3); //[mm] - yout = ((d - s) / 2) * (ch2 + ch3 - ch0 - ch1) / (ch0 + ch1 + ch2 + ch3); //[mm] + float xout = ((D - S) / 2) * (ch2 - ch3 - ch0 + ch1) / (ch0 + ch1 + ch2 + ch3); //[mm] + float yout = ((D - S) / 2) * (ch2 + ch3 - ch0 - ch1) / (ch0 + ch1 + ch2 + ch3); //[mm] // Calculation of the angles - alphaBetaRaw[0] = atan2(xout, h) * (180 / M_PI); //[°] - alphaBetaRaw[1] = atan2(yout, h) * (180 / M_PI); //[°] -} - -void SusConverter::calibration(const float coeffAlpha[9][10], const float coeffBeta[9][10]) { - uint8_t index, k, l; - - // while loop iterates above all calibration cells to use the different calibration functions in - // each cell - k = 0; - while (k < 3) { - k++; - l = 0; - while (l < 3) { - l++; - // if-condition to check in which cell the data point has to be - if ((alphaBetaRaw[0] > ((completeCellWidth * ((k - 1) / 3.)) - halfCellWidth) && - alphaBetaRaw[0] < ((completeCellWidth * (k / 3.)) - halfCellWidth)) && - (alphaBetaRaw[1] > ((completeCellWidth * ((l - 1) / 3.)) - halfCellWidth) && - alphaBetaRaw[1] < ((completeCellWidth * (l / 3.)) - halfCellWidth))) { - index = (3 * (k - 1) + l) - 1; // calculate the index of the datapoint for the right cell - alphaBetaCalibrated[0] = - coeffAlpha[index][0] + coeffAlpha[index][1] * alphaBetaRaw[0] + - coeffAlpha[index][2] * alphaBetaRaw[1] + - coeffAlpha[index][3] * alphaBetaRaw[0] * alphaBetaRaw[0] + - coeffAlpha[index][4] * alphaBetaRaw[0] * alphaBetaRaw[1] + - coeffAlpha[index][5] * alphaBetaRaw[1] * alphaBetaRaw[1] + - coeffAlpha[index][6] * alphaBetaRaw[0] * alphaBetaRaw[0] * alphaBetaRaw[0] + - coeffAlpha[index][7] * alphaBetaRaw[0] * alphaBetaRaw[0] * alphaBetaRaw[1] + - coeffAlpha[index][8] * alphaBetaRaw[0] * alphaBetaRaw[1] * alphaBetaRaw[1] + - coeffAlpha[index][9] * alphaBetaRaw[1] * alphaBetaRaw[1] * alphaBetaRaw[1]; //[°] - alphaBetaCalibrated[1] = - coeffBeta[index][0] + coeffBeta[index][1] * alphaBetaRaw[0] + - coeffBeta[index][2] * alphaBetaRaw[1] + - coeffBeta[index][3] * alphaBetaRaw[0] * alphaBetaRaw[0] + - coeffBeta[index][4] * alphaBetaRaw[0] * alphaBetaRaw[1] + - coeffBeta[index][5] * alphaBetaRaw[1] * alphaBetaRaw[1] + - coeffBeta[index][6] * alphaBetaRaw[0] * alphaBetaRaw[0] * alphaBetaRaw[0] + - coeffBeta[index][7] * alphaBetaRaw[0] * alphaBetaRaw[0] * alphaBetaRaw[1] + - coeffBeta[index][8] * alphaBetaRaw[0] * alphaBetaRaw[1] * alphaBetaRaw[1] + - coeffBeta[index][9] * alphaBetaRaw[1] * alphaBetaRaw[1] * alphaBetaRaw[1]; //[°] - } - } - } -} - -float* SusConverter::calculateSunVector() { - // Calculate the normalized Sun Vector - sunVectorSensorFrame[0] = -(tan(alphaBetaCalibrated[0] * (M_PI / 180)) / - (sqrt((powf(tan(alphaBetaCalibrated[0] * (M_PI / 180)), 2)) + - powf(tan((alphaBetaCalibrated[1] * (M_PI / 180))), 2) + (1)))); - sunVectorSensorFrame[1] = -(tan(alphaBetaCalibrated[1] * (M_PI / 180)) / - (sqrt(powf((tan(alphaBetaCalibrated[0] * (M_PI / 180))), 2) + - powf(tan((alphaBetaCalibrated[1] * (M_PI / 180))), 2) + (1)))); - sunVectorSensorFrame[2] = - -(-1 / (sqrt(powf((tan(alphaBetaCalibrated[0] * (M_PI / 180))), 2) + - powf((tan(alphaBetaCalibrated[1] * (M_PI / 180))), 2) + (1)))); - - return sunVectorSensorFrame; -} - -float* SusConverter::getSunVectorSensorFrame(const uint16_t susChannel[6], - const float coeffAlpha[9][10], - const float coeffBeta[9][10]) { - calcAngle(susChannel); - calibration(coeffAlpha, coeffBeta); - return calculateSunVector(); + sunVectorSensorFrame[0] = -xout; + sunVectorSensorFrame[1] = -yout; + sunVectorSensorFrame[2] = H; + VectorOperations::normalize(sunVectorSensorFrame, sunVectorSensorFrame, 3); } diff --git a/mission/controller/acs/SusConverter.h b/mission/controller/acs/SusConverter.h index 046b0ca8..8a6c279b 100644 --- a/mission/controller/acs/SusConverter.h +++ b/mission/controller/acs/SusConverter.h @@ -1,8 +1,4 @@ -#ifndef MISSION_CONTROLLER_ACS_SUSCONVERTER_H_ -#define MISSION_CONTROLLER_ACS_SUSCONVERTER_H_ - -#include -#include +#include #include "AcsParameters.h" @@ -10,41 +6,26 @@ class SusConverter { public: SusConverter() {} - bool checkSunSensorData(const uint16_t susChannel[6]); - - void calcAngle(const uint16_t susChannel[6]); - void calibration(const float coeffAlpha[9][10], const float coeffBeta[9][10]); - float* calculateSunVector(); - - float* getSunVectorSensorFrame(const uint16_t susChannel[6], const float coeffAlpha[9][10], - const float coeffBeta[9][10]); + uint64_t checkSunSensorData(const uint16_t susChannel[6]); + bool checkValidity(bool* susValid, const uint64_t brightness[12], const float threshold); + void calculateSunVector(float* sunVectorSensorFrame, const uint16_t susChannel[6]); private: - float alphaBetaRaw[2]; //[°] - float alphaBetaCalibrated[2]; //[°] - float sunVectorSensorFrame[3]; //[-] - - bool validFlag[12] = {returnvalue::OK, returnvalue::OK, returnvalue::OK, returnvalue::OK, - returnvalue::OK, returnvalue::OK, returnvalue::OK, returnvalue::OK, - returnvalue::OK, returnvalue::OK, returnvalue::OK, returnvalue::OK}; - static const uint8_t GNDREF = 4; - uint16_t susChannelValueCheckHigh = - 4096; //=2^12[Bit]high borderline for the channel values of one sun sensor for validity Check - uint8_t susChannelValueCheckLow = - 0; //[Bit]low borderline for the channel values of one sun sensor for validity Check - uint16_t susChannelValueSumHigh = - 100; // 4096[Bit]high borderline for check if the sun sensor is illuminated by the sun or by - // the reflection of sunlight from the moon/earth - uint8_t susChannelValueSumLow = - 0; //[Bit]low borderline for check if the sun sensor is illuminated - // by the sun or by the reflection of sunlight from the moon/earth - uint8_t completeCellWidth = 140, - halfCellWidth = 70; //[°] Width of the calibration cells --> necessary for checking in - // which cell a data point should be - uint16_t susChannelValueSum = 0; + // =2^12[Bit]high borderline for the channel values of one sun sensor for validity Check + static constexpr uint16_t SUS_CHANNEL_VALUE_HIGH = 4096; + // [Bit]low borderline for the channel values of one sun sensor for validity Check + static constexpr uint8_t SUS_CHANNEL_VALUE_LOW = 0; + // 4096[Bit]high borderline for check if the sun sensor is illuminated by the sun or by the + // reflection of sunlight from the moon/earth + static constexpr uint16_t SUS_ALBEDO_CHECK = 1000; + // [Bit]low borderline for check if the sun sensor is illuminated by the sun or by the reflection + // of sunlight from the moon/earth + static constexpr uint8_t SUS_CHANNEL_SUM_LOW = 0; + + static constexpr float S = 0.03; // S=[mm] gap between diodes + static constexpr float D = 5; // D=[mm] edge length of the quadratic aperture + static constexpr float H = 2.5; // H=[mm] distance between diodes and aperture AcsParameters acsParameters; }; - -#endif /* MISSION_CONTROLLER_ACS_SUSCONVERTER_H_ */ diff --git a/mission/controller/acs/control/SafeCtrl.cpp b/mission/controller/acs/control/SafeCtrl.cpp index 43677ccf..8ad8b578 100644 --- a/mission/controller/acs/control/SafeCtrl.cpp +++ b/mission/controller/acs/control/SafeCtrl.cpp @@ -9,20 +9,36 @@ SafeCtrl::SafeCtrl(AcsParameters *acsParameters_) { acsParameters = acsParameter SafeCtrl::~SafeCtrl() {} -acs::SafeModeStrategy SafeCtrl::safeCtrlStrategy(const bool magFieldValid, const bool mekfValid, - const bool satRotRateValid, const bool sunDirValid, - const uint8_t mekfEnabled, - const uint8_t dampingEnabled) { +acs::SafeModeStrategy SafeCtrl::safeCtrlStrategy( + const bool magFieldValid, const bool mekfValid, const bool satRotRateValid, + const bool sunDirValid, const bool fusedRateSplitValid, const bool fusedRateTotalValid, + const uint8_t mekfEnabled, const uint8_t gyrEnabled, const uint8_t dampingEnabled) { if (not magFieldValid) { return acs::SafeModeStrategy::SAFECTRL_NO_MAG_FIELD_FOR_CONTROL; } else if (mekfEnabled and mekfValid) { - return acs::SafeModeStrategy::SAFECTRL_ACTIVE_MEKF; - } else if (satRotRateValid and sunDirValid) { - return acs::SafeModeStrategy::SAFECTRL_WITHOUT_MEKF; - } else if (dampingEnabled and satRotRateValid and not sunDirValid) { - return acs::SafeModeStrategy::SAFECTRL_ECLIPSE_DAMPING; - } else if (not dampingEnabled and satRotRateValid and not sunDirValid) { - return acs::SafeModeStrategy::SAFECTRL_ECLIPSE_IDELING; + return acs::SafeModeStrategy::SAFECTRL_MEKF; + } else if (sunDirValid) { + if (gyrEnabled and satRotRateValid) { + return acs::SafeModeStrategy::SAFECTRL_GYR; + } else if (not gyrEnabled and fusedRateSplitValid) { + return acs::SafeModeStrategy::SAFECTRL_SUSMGM; + } else { + return acs::SafeModeStrategy::SAFECTRL_NO_SENSORS_FOR_CONTROL; + } + } else if (not sunDirValid) { + if (dampingEnabled) { + if (gyrEnabled and satRotRateValid) { + return acs::SafeModeStrategy::SAFECTRL_ECLIPSE_DAMPING_GYR; + } else if (not gyrEnabled and satRotRateValid and fusedRateTotalValid) { + return acs::SafeModeStrategy::SAFECTRL_ECLIPSE_DAMPING_SUSMGM; + } else { + return acs::SafeModeStrategy::SAFECTRL_NO_SENSORS_FOR_CONTROL; + } + } else if (not dampingEnabled and satRotRateValid) { + return acs::SafeModeStrategy::SAFECTRL_ECLIPSE_IDELING; + } else { + return acs::SafeModeStrategy::SAFECTRL_NO_SENSORS_FOR_CONTROL; + } } else { return acs::SafeModeStrategy::SAFECTRL_NO_SENSORS_FOR_CONTROL; } @@ -43,8 +59,7 @@ void SafeCtrl::safeMekf(const double *magFieldB, const double *satRotRateB, errorAngle = acos(dotSun); splitRotationalRate(satRotRateB, sunDirB); - calculateRotationalRateTorque(sunDirB, sunDirRefB, errorAngle, - acsParameters->safeModeControllerParameters.k_parallelMekf, + calculateRotationalRateTorque(acsParameters->safeModeControllerParameters.k_parallelMekf, acsParameters->safeModeControllerParameters.k_orthoMekf); calculateAngleErrorTorque(sunDirB, sunDirRefB, acsParameters->safeModeControllerParameters.k_alignMekf); @@ -57,9 +72,8 @@ void SafeCtrl::safeMekf(const double *magFieldB, const double *satRotRateB, calculateMagneticMoment(magMomB); } -void SafeCtrl::safeNonMekf(const double *magFieldB, const double *satRotRateB, - const double *sunDirB, const double *sunDirRefB, double *magMomB, - double &errorAngle) { +void SafeCtrl::safeGyr(const double *magFieldB, const double *satRotRateB, const double *sunDirB, + const double *sunDirRefB, double *magMomB, double &errorAngle) { // convert magFieldB from uT to T VectorOperations::mulScalar(magFieldB, 1e-6, magFieldBT, 3); @@ -68,11 +82,10 @@ void SafeCtrl::safeNonMekf(const double *magFieldB, const double *satRotRateB, errorAngle = acos(dotSun); splitRotationalRate(satRotRateB, sunDirB); - calculateRotationalRateTorque(sunDirB, sunDirRefB, errorAngle, - acsParameters->safeModeControllerParameters.k_parallelNonMekf, - acsParameters->safeModeControllerParameters.k_orthoNonMekf); + calculateRotationalRateTorque(acsParameters->safeModeControllerParameters.k_parallelGyr, + acsParameters->safeModeControllerParameters.k_orthoGyr); calculateAngleErrorTorque(sunDirB, sunDirRefB, - acsParameters->safeModeControllerParameters.k_alignNonMekf); + acsParameters->safeModeControllerParameters.k_alignGyr); // sum of all torques for (uint8_t i = 0; i < 3; i++) { @@ -82,8 +95,33 @@ void SafeCtrl::safeNonMekf(const double *magFieldB, const double *satRotRateB, calculateMagneticMoment(magMomB); } -void SafeCtrl::safeRateDamping(const double *magFieldB, const double *satRotRateB, - const double *sunDirRefB, double *magMomB, double &errorAngle) { +void SafeCtrl::safeSusMgm(const double *magFieldB, const double *rotRateParallelB, + const double *rotRateOrthogonalB, const double *sunDirB, + const double *sunDirRefB, double *magMomB, double &errorAngle) { + // convert magFieldB from uT to T + VectorOperations::mulScalar(magFieldB, 1e-6, magFieldBT, 3); + + // calculate error angle between sunDirRef and sunDir + double dotSun = VectorOperations::dot(sunDirRefB, sunDirB); + errorAngle = acos(dotSun); + + std::memcpy(satRotRateParallelB, rotRateParallelB, sizeof(satRotRateParallelB)); + std::memcpy(satRotRateOrthogonalB, rotRateOrthogonalB, sizeof(satRotRateOrthogonalB)); + calculateRotationalRateTorque(acsParameters->safeModeControllerParameters.k_parallelSusMgm, + acsParameters->safeModeControllerParameters.k_orthoSusMgm); + calculateAngleErrorTorque(sunDirB, sunDirRefB, + acsParameters->safeModeControllerParameters.k_alignSusMgm); + + // sum of all torques + for (uint8_t i = 0; i < 3; i++) { + cmdTorque[i] = cmdAlign[i] + cmdOrtho[i] + cmdParallel[i]; + } + + calculateMagneticMoment(magMomB); +} + +void SafeCtrl::safeRateDampingGyr(const double *magFieldB, const double *satRotRateB, + const double *sunDirRefB, double *magMomB, double &errorAngle) { // convert magFieldB from uT to T VectorOperations::mulScalar(magFieldB, 1e-6, magFieldBT, 3); @@ -91,9 +129,28 @@ void SafeCtrl::safeRateDamping(const double *magFieldB, const double *satRotRate errorAngle = NAN; splitRotationalRate(satRotRateB, sunDirRefB); - calculateRotationalRateTorque(sunDirRefB, sunDirRefB, errorAngle, - acsParameters->safeModeControllerParameters.k_parallelNonMekf, - acsParameters->safeModeControllerParameters.k_orthoNonMekf); + calculateRotationalRateTorque(acsParameters->safeModeControllerParameters.k_parallelGyr, + acsParameters->safeModeControllerParameters.k_orthoGyr); + + // sum of all torques + VectorOperations::add(cmdParallel, cmdOrtho, cmdTorque, 3); + + // calculate magnetic moment to command + calculateMagneticMoment(magMomB); +} + +void SafeCtrl::safeRateDampingSusMgm(const double *magFieldB, const double *satRotRateB, + const double *sunDirRefB, double *magMomB, + double &errorAngle) { + // convert magFieldB from uT to T + VectorOperations::mulScalar(magFieldB, 1e-6, magFieldBT, 3); + + // no error angle available for eclipse + errorAngle = NAN; + + splitRotationalRate(satRotRateB, sunDirRefB); + calculateRotationalRateTorque(acsParameters->safeModeControllerParameters.k_parallelSusMgm, + acsParameters->safeModeControllerParameters.k_orthoSusMgm); // sum of all torques VectorOperations::add(cmdParallel, cmdOrtho, cmdTorque, 3); @@ -110,9 +167,7 @@ void SafeCtrl::splitRotationalRate(const double *satRotRateB, const double *sunD VectorOperations::subtract(satRotRateB, satRotRateParallelB, satRotRateOrthogonalB, 3); } -void SafeCtrl::calculateRotationalRateTorque(const double *sunDirB, const double *sunDirRefB, - double &errorAngle, const double gainParallel, - const double gainOrtho) { +void SafeCtrl::calculateRotationalRateTorque(const double gainParallel, const double gainOrtho) { // calculate torque for parallel rotational rate VectorOperations::mulScalar(satRotRateParallelB, -gainParallel, cmdParallel, 3); diff --git a/mission/controller/acs/control/SafeCtrl.h b/mission/controller/acs/control/SafeCtrl.h index 12f9ddb0..55bcbf08 100644 --- a/mission/controller/acs/control/SafeCtrl.h +++ b/mission/controller/acs/control/SafeCtrl.h @@ -14,23 +14,34 @@ class SafeCtrl { acs::SafeModeStrategy safeCtrlStrategy(const bool magFieldValid, const bool mekfValid, const bool satRotRateValid, const bool sunDirValid, - const uint8_t mekfEnabled, const uint8_t dampingEnabled); + const bool fusedRateSplitValid, + const bool fusedRateTotalValid, const uint8_t mekfEnabled, + const uint8_t gyrEnabled, const uint8_t dampingEnabled); void safeMekf(const double *magFieldB, const double *satRotRateB, const double *sunDirModelI, const double *quatBI, const double *sunDirRefB, double *magMomB, double &errorAngle); - void safeNonMekf(const double *magFieldB, const double *satRotRateB, const double *sunDirB, - const double *sunDirRefB, double *magMomB, double &errorAngle); + void safeGyr(const double *magFieldB, const double *satRotRateB, const double *sunDirB, + const double *sunDirRefB, double *magMomB, double &errorAngle); - void safeRateDamping(const double *magFieldB, const double *satRotRateB, const double *sunDirRefB, - double *magMomB, double &errorAngle); + void safeSusMgm(const double *magFieldB, const double *rotRateParallelB, + const double *rotRateOrthogonalB, const double *sunDirB, const double *sunDirRefB, + double *magMomB, double &errorAngle); + + void safeRateDampingGyr(const double *magFieldB, const double *satRotRateB, + const double *sunDirRefB, double *magMomB, double &errorAngle); + + void safeRateDampingSusMgm(const double *magFieldB, const double *satRotRateB, + const double *sunDirRefB, double *magMomB, double &errorAngle); void splitRotationalRate(const double *satRotRateB, const double *sunDirB); - void calculateRotationalRateTorque(const double *sunDirB, const double *sunDirRefB, - double &errorAngle, const double gainParallel, - const double gainOrtho); + void calculateRotationalRates(const double *magFieldB, const double *magRateB, + const double *sunDirB, const double *sunRateB, + double *fusedRotRate); + + void calculateRotationalRateTorque(const double gainParallel, const double gainOrtho); void calculateAngleErrorTorque(const double *sunDirB, const double *sunDirRefB, const double gainAlign); diff --git a/mission/controller/controllerdefinitions/AcsCtrlDefinitions.h b/mission/controller/controllerdefinitions/AcsCtrlDefinitions.h index c8dbd275..86866c3f 100644 --- a/mission/controller/controllerdefinitions/AcsCtrlDefinitions.h +++ b/mission/controller/controllerdefinitions/AcsCtrlDefinitions.h @@ -18,7 +18,8 @@ enum SetIds : uint32_t { GPS_PROCESSED_DATA, MEKF_DATA, CTRL_VAL_DATA, - ACTUATOR_CMD_DATA + ACTUATOR_CMD_DATA, + FUSED_ROTATION_RATE_DATA, }; enum PoolIds : lp_id_t { @@ -103,6 +104,10 @@ enum PoolIds : lp_id_t { RW_TARGET_TORQUE, RW_TARGET_SPEED, MTQ_TARGET_DIPOLE, + // Fused Rotation Rate + ROT_RATE_ORTHOGONAL, + ROT_RATE_PARALLEL, + ROT_RATE_TOTAL, }; static constexpr uint8_t MGM_SET_RAW_ENTRIES = 6; @@ -115,6 +120,7 @@ static constexpr uint8_t GPS_SET_PROCESSED_ENTRIES = 5; static constexpr uint8_t MEKF_SET_ENTRIES = 3; static constexpr uint8_t CTRL_VAL_SET_ENTRIES = 5; static constexpr uint8_t ACT_CMD_SET_ENTRIES = 3; +static constexpr uint8_t FUSED_ROT_RATE_SET_ENTRIES = 3; /** * @brief Raw MGM sensor data. Includes the IMTQ sensor data and actuator status. @@ -273,6 +279,19 @@ class ActuatorCmdData : public StaticLocalDataSet { private: }; +class FusedRotRateData : public StaticLocalDataSet { + public: + FusedRotRateData(HasLocalDataPoolIF* hkOwner) + : StaticLocalDataSet(hkOwner, FUSED_ROTATION_RATE_DATA) {} + + lp_vec_t rotRateOrthogonal = + lp_vec_t(sid.objectId, ROT_RATE_ORTHOGONAL, this); + lp_vec_t rotRateParallel = lp_vec_t(sid.objectId, ROT_RATE_PARALLEL, this); + lp_vec_t rotRateTotal = lp_vec_t(sid.objectId, ROT_RATE_TOTAL, this); + + private: +}; + } // namespace acsctrl #endif /* MISSION_CONTROLLER_CONTROLLERDEFINITIONS_ACSCTRLDEFINITIONS_H_ */ diff --git a/mission/controller/tcsDefs.h b/mission/controller/tcsDefs.h index e2efd98c..f5d9d180 100644 --- a/mission/controller/tcsDefs.h +++ b/mission/controller/tcsDefs.h @@ -9,6 +9,85 @@ namespace tcsCtrl { +/** + * NOP Limit: Hard limit for device, usually from datasheet. Device damage is possible lif NOP limit + * is exceeded. + * OP Limit: Soft limit. Device should be switched off or TCS controller should take action if the + * limit is exceeded to avoid reaching NOP limit + */ +struct TempLimits { + TempLimits(float nopLowerLimit, float opLowerLimit, float cutOffLimit, float opUpperLimit, + float nopUpperLimit) + : opLowerLimit(opLowerLimit), + opUpperLimit(opUpperLimit), + cutOffLimit(cutOffLimit), + nopLowerLimit(nopLowerLimit), + nopUpperLimit(nopUpperLimit) {} + float opLowerLimit; + float opUpperLimit; + float cutOffLimit; + float nopLowerLimit; + float nopUpperLimit; +}; + +/** + * Abstraction for the state of a single thermal component + */ +struct ThermalState { + uint8_t noSensorAvailableCounter; + // Which sensor is used for this component? + uint8_t sensorIndex = 0; + // Is heating on for that thermal module? + bool heating = false; + // Which switch is being used for heating the component + heater::Switch heaterSwitch = heater::Switch::HEATER_NONE; + // Heater start time and end times as UNIX seconds. Please note that these times will be updated + // when a switch command is sent, with no guarantess that the heater actually went on. + uint32_t heaterStartTime = 0; + uint32_t heaterEndTime = 0; +}; + +/** + * Abstraction for the state of a single heater. + */ +struct HeaterState { + bool switchTransition = false; + heater::SwitchState target = heater::SwitchState::OFF; + uint8_t heaterSwitchControlCycles = 0; + bool trackHeaterMaxBurnTime = false; + Countdown heaterOnMaxBurnTime; +}; + +using HeaterSwitchStates = std::array; + +enum ThermalComponents : uint8_t { + NONE = 0, + ACS_BOARD = 1, + MGT = 2, + RW = 3, + STR = 4, + IF_BOARD = 5, + TCS_BOARD = 6, + OBC = 7, + LEGACY_OBCIF_BOARD = 8, + SBAND_TRANSCEIVER = 9, + PCDUP60_BOARD = 10, + PCDUACU = 11, + PCDUPDU = 12, + PLPCDU_BOARD = 13, + PLOCMISSION_BOARD = 14, + PLOCPROCESSING_BOARD = 15, + DAC = 16, + CAMERA = 17, + DRO = 18, + X8 = 19, + HPA = 20, + TX = 21, + MPA = 22, + SCEX_BOARD = 23, + NUM_THERMAL_COMPONENTS +}; + static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::TCS_CONTROLLER; static constexpr Event NO_VALID_SENSOR_TEMPERATURE = MAKE_EVENT(0, severity::MEDIUM); static constexpr Event NO_HEALTHY_HEATER_AVAILABLE = MAKE_EVENT(1, severity::MEDIUM); @@ -18,6 +97,12 @@ static constexpr Event CAMERA_OVERHEATING = MAKE_EVENT(5, severity::HIGH); static constexpr Event PCDU_SYSTEM_OVERHEATING = MAKE_EVENT(6, severity::HIGH); static constexpr Event HEATER_NOT_OFF_FOR_OFF_MODE = MAKE_EVENT(7, severity::MEDIUM); static constexpr Event MGT_OVERHEATING = MAKE_EVENT(8, severity::HIGH); +//! [EXPORT] : [COMMENT] P1: Module index. P2: Heater index +static constexpr Event TCS_SWITCHING_HEATER_ON = MAKE_EVENT(9, severity::INFO); +//! [EXPORT] : [COMMENT] P1: Module index. P2: Heater index +static constexpr Event TCS_SWITCHING_HEATER_OFF = MAKE_EVENT(10, severity::INFO); +//! [EXPORT] : [COMMENT] P1: Heater index. P2: Maximum burn time for heater. +static constexpr Event TCS_HEATER_MAX_BURN_TIME_REACHED = MAKE_EVENT(11, severity::MEDIUM); enum SetId : uint32_t { SENSOR_TEMPERATURES = 0, @@ -25,6 +110,7 @@ enum SetId : uint32_t { SUS_TEMPERATURES = 2, COMPONENT_TEMPERATURES = 3, HEATER_SET = 4, + TCS_CTRL_INFO = 5 }; enum PoolIds : lp_id_t { @@ -92,7 +178,13 @@ enum PoolIds : lp_id_t { TEMP_ADC_PAYLOAD_PCDU, HEATER_SWITCH_LIST, - HEATER_CURRENT + HEATER_CURRENT, + + HEATER_ON_FOR_COMPONENT_VEC, + SENSOR_USED_FOR_TCS_CTRL, + HEATER_IDX_USED_FOR_TCS_CTRL, + HEATER_START_TIME, + HEATER_END_TIME }; static const uint8_t ENTRIES_SENSOR_TEMPERATURE_SET = 25; @@ -232,6 +324,29 @@ class HeaterInfo : public StaticLocalDataSet<3> { lp_var_t heaterCurrent = lp_var_t(sid.objectId, PoolIds::HEATER_CURRENT, this); }; +class TcsCtrlInfo : public StaticLocalDataSet<6> { + public: + explicit TcsCtrlInfo(HasLocalDataPoolIF* owner) : StaticLocalDataSet(owner, TCS_CTRL_INFO) {} + + explicit TcsCtrlInfo(object_id_t objectId) : StaticLocalDataSet(sid_t(objectId, TCS_CTRL_INFO)) {} + + lp_vec_t heatingOnVec = + lp_vec_t( + sid.objectId, PoolIds::HEATER_ON_FOR_COMPONENT_VEC, this); + lp_vec_t sensorIdxUsedForTcsCtrl = + lp_vec_t(sid.objectId, + PoolIds::SENSOR_USED_FOR_TCS_CTRL, this); + lp_vec_t heaterSwitchIdx = + lp_vec_t( + sid.objectId, PoolIds::HEATER_IDX_USED_FOR_TCS_CTRL, this); + lp_vec_t heaterStartTimes = + lp_vec_t(sid.objectId, PoolIds::HEATER_START_TIME, + this); + lp_vec_t heaterEndTimes = + lp_vec_t(sid.objectId, PoolIds::HEATER_END_TIME, + this); +}; + } // namespace tcsCtrl #endif /* MISSION_CONTROLLER_CONTROLLERDEFINITIONS_THERMALCONTROLLERDEFINITIONS_H_ */ diff --git a/mission/genericFactory.cpp b/mission/genericFactory.cpp index cbe78c7f..3d9d0c81 100644 --- a/mission/genericFactory.cpp +++ b/mission/genericFactory.cpp @@ -91,19 +91,21 @@ EiveFaultHandler EIVE_FAULT_HANDLER; } // namespace cfdp std::atomic_bool tcs::TCS_BOARD_SHORTLY_UNAVAILABLE = false; +std::atomic_bool core::SAVE_PUS_SEQUENCE_COUNT = false; +std::atomic_bool core::SAVE_CFDP_SEQUENCE_COUNT = false; void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFunnel** pusFunnel, CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan, StorageManagerIF** ipcStore, StorageManagerIF** tmStore, PersistentTmStores& stores, - uint32_t eventManagerQueueDepth) { + uint32_t eventManagerQueueDepth, bool enableHkSets) { // Framework objects new EventManager(objects::EVENT_MANAGER, eventManagerQueueDepth); auto healthTable = new HealthTable(objects::HEALTH_TABLE); if (healthTable_ != nullptr) { *healthTable_ = healthTable; } - new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); + new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER, 5, enableHkSets, 120); new VerificationReporter(); auto* timeStamper = new CdsShortTimeStamper(objects::TIME_STAMPER); StorageManagerIF* tcStore; @@ -155,9 +157,11 @@ void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFun new PusDistributor(config::EIVE_PUS_APID, objects::PUS_PACKET_DISTRIBUTOR, ccsdsDistrib); PusTmFunnel::FunnelCfg pusFunnelCfg(objects::PUS_TM_FUNNEL, "PusTmFunnel", **tmStore, **ipcStore, - config::MAX_PUS_FUNNEL_QUEUE_DEPTH); + config::MAX_PUS_FUNNEL_QUEUE_DEPTH, sdcMan, + config::PUS_SEQUENCE_COUNT_FILE, + core::SAVE_PUS_SEQUENCE_COUNT); // The PUS funnel routes all live TM to the live destinations and to the TM stores. - *pusFunnel = new PusTmFunnel(pusFunnelCfg, *ramToFileStore, *timeStamper, sdcMan); + *pusFunnel = new PusTmFunnel(pusFunnelCfg, *ramToFileStore, *timeStamper); // MISC store and PUS funnel to MISC store routing { @@ -216,7 +220,9 @@ void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFun stores.cfdpStore->getReportReceptionQueue(0)); } PusTmFunnel::FunnelCfg cfdpFunnelCfg(objects::CFDP_TM_FUNNEL, "CfdpTmFunnel", **tmStore, - **ipcStore, config::MAX_CFDP_FUNNEL_QUEUE_DEPTH); + **ipcStore, config::MAX_CFDP_FUNNEL_QUEUE_DEPTH, sdcMan, + config::CFDP_SEQUENCE_COUNT_FILE, + core::SAVE_CFDP_SEQUENCE_COUNT); *cfdpFunnel = new CfdpTmFunnel(cfdpFunnelCfg, stores.cfdpStore->getReportReceptionQueue(0), *ramToFileStore, config::EIVE_CFDP_APID); @@ -305,9 +311,9 @@ void ObjectFactory::createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF& heaterHandler->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); } -void ObjectFactory::createThermalController(HeaterHandler& heaterHandler) { +void ObjectFactory::createThermalController(HeaterHandler& heaterHandler, bool pollPlPcduTmp1) { auto* tcsCtrl = new ThermalController(objects::THERMAL_CONTROLLER, heaterHandler, - tcs::TCS_BOARD_SHORTLY_UNAVAILABLE); + tcs::TCS_BOARD_SHORTLY_UNAVAILABLE, pollPlPcduTmp1); tcsCtrl->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); } void ObjectFactory::createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch, diff --git a/mission/genericFactory.h b/mission/genericFactory.h index a3a52704..7845c140 100644 --- a/mission/genericFactory.h +++ b/mission/genericFactory.h @@ -45,11 +45,12 @@ namespace ObjectFactory { void produceGenericObjects(HealthTableIF** healthTable, PusTmFunnel** pusFunnel, CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan, StorageManagerIF** ipcStore, StorageManagerIF** tmStore, - PersistentTmStores& stores, uint32_t eventManagerQueueDepth); + PersistentTmStores& stores, uint32_t eventManagerQueueDepth, + bool enableHkSets); void createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF& pwrSwitcher, HeaterHandler*& heaterHandler); -void createThermalController(HeaterHandler& heaterHandler); +void createThermalController(HeaterHandler& heaterHandler, bool pollPlPcduTmp1); void createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch, std::array rws, std::array rwIds); void createSusAssy(PowerSwitchIF& pwrSwitcher, std::array suses); diff --git a/mission/payload/PayloadPcduHandler.cpp b/mission/payload/PayloadPcduHandler.cpp index 9223f454..1e6d16db 100644 --- a/mission/payload/PayloadPcduHandler.cpp +++ b/mission/payload/PayloadPcduHandler.cpp @@ -1,7 +1,9 @@ -#include +#include +#include #include #include "OBSWConfig.h" +#include "fsfw/thermal/tcsDefinitions.h" #ifdef XIPHOS_Q7S #include @@ -64,6 +66,16 @@ void PayloadPcduHandler::doShutDown() { return; } state = States::PL_PCDU_OFF; + quickTransitionAlreadyCalled = false; + { + PoolReadGuard pg(&adcSet); + adcSet.setReportingEnabled(false); + adcSet.tempC = thermal::INVALID_TEMPERATURE; + + std::memset(adcSet.channels.value, 0, sizeof(adcSet.channels.value)); + std::memset(adcSet.processed.value, 0, sizeof(adcSet.processed.value)); + adcSet.setValidity(false, true); + } // No need to set mode _MODE_POWER_DOWN, power switching was already handled setMode(MODE_OFF); } @@ -73,14 +85,7 @@ void PayloadPcduHandler::doTransition(Mode_t modeFrom, Submode_t subModeFrom) { stateMachineToNormal(modeFrom, subModeFrom); return; } else if (getMode() == _MODE_TO_ON and modeFrom == MODE_NORMAL) { - gpioIF->pullLow(gpioIds::PLPCDU_ENB_HPA); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_MPA); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_X8); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_DRO); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT0); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT1); + pullAllGpiosLow(200); state = States::STACK_5V_CORRECT; } DeviceHandlerBase::doTransition(modeFrom, subModeFrom); @@ -89,6 +94,11 @@ void PayloadPcduHandler::doTransition(Mode_t modeFrom, Submode_t subModeFrom) { ReturnValue_t PayloadPcduHandler::stateMachineToNormal(Mode_t modeFrom, Submode_t subModeFrom) { using namespace plpcdu; bool doFinish = true; + if (toNormalOneShot) { + PoolReadGuard pg(&adcSet); + adcSet.setReportingEnabled(true); + toNormalOneShot = false; + } if (((getSubmode() >> SOLID_STATE_RELAYS_ADC_ON) & 0b1) == 1) { if (state == States::PL_PCDU_OFF) { sif::error << "PayloadPcduHandler::stateMachineToNormal: Unexpected state PL_PCDU_OFF" @@ -114,23 +124,23 @@ ReturnValue_t PayloadPcduHandler::stateMachineToNormal(Mode_t modeFrom, Submode_ state = States::ON_TRANS_ADC_CLOSE_ZERO; adcCountdown.setTimeout(50); adcCountdown.resetTimer(); - adcState = AdcStates::BOOT_DELAY; + adcState = AdcState::BOOT_DELAY; doFinish = false; // If the values are not close to zero, we should not allow transition monMode = MonitoringMode::CLOSE_TO_ZERO; } } if (state == States::ON_TRANS_ADC_CLOSE_ZERO) { - if (adcState == AdcStates::BOOT_DELAY) { + if (adcState == AdcState::BOOT_DELAY) { doFinish = false; if (adcCountdown.hasTimedOut()) { - adcState = AdcStates::SEND_SETUP; + adcState = AdcState::SEND_SETUP; adcCmdExecuted = false; } } - if (adcState == AdcStates::SEND_SETUP) { + if (adcState == AdcState::SEND_SETUP) { if (adcCmdExecuted) { - adcState = AdcStates::NORMAL; + adcState = AdcState::NORMAL; doFinish = true; adcCountdown.setTimeout(100); adcCountdown.resetTimer(); @@ -167,6 +177,7 @@ ReturnValue_t PayloadPcduHandler::stateMachineToNormal(Mode_t modeFrom, Submode_ switchHandler(MPA_ON, gpioIds::PLPCDU_ENB_MPA, "MPA"); switchHandler(HPA_ON, gpioIds::PLPCDU_ENB_HPA, "HPA"); if (doFinish) { + toNormalOneShot = true; setMode(MODE_NORMAL); } return returnvalue::OK; @@ -174,11 +185,11 @@ ReturnValue_t PayloadPcduHandler::stateMachineToNormal(Mode_t modeFrom, Submode_ ReturnValue_t PayloadPcduHandler::buildNormalDeviceCommand(DeviceCommandId_t* id) { switch (adcState) { - case (AdcStates::SEND_SETUP): { + case (AdcState::SEND_SETUP): { *id = plpcdu::SETUP_CMD; return buildCommandFromCommand(*id, nullptr, 0); } - case (AdcStates::NORMAL): { + case (AdcState::NORMAL): { *id = plpcdu::READ_WITH_TEMP_EXT; return buildCommandFromCommand(*id, nullptr, 0); } @@ -190,7 +201,7 @@ ReturnValue_t PayloadPcduHandler::buildNormalDeviceCommand(DeviceCommandId_t* id } ReturnValue_t PayloadPcduHandler::buildTransitionDeviceCommand(DeviceCommandId_t* id) { - if (adcState == AdcStates::SEND_SETUP) { + if (adcState == AdcState::SEND_SETUP) { *id = plpcdu::SETUP_CMD; return buildCommandFromCommand(*id, nullptr, 0); } @@ -211,9 +222,9 @@ void PayloadPcduHandler::updateSwitchGpio(gpioId_t id, gpio::Levels level) { } void PayloadPcduHandler::fillCommandAndReplyMap() { - insertInCommandAndReplyMap(plpcdu::READ_CMD, 2, &adcSet); - insertInCommandAndReplyMap(plpcdu::READ_TEMP_EXT, 1, &adcSet); - insertInCommandAndReplyMap(plpcdu::READ_WITH_TEMP_EXT, 1, &adcSet); + insertInCommandAndReplyMap(plpcdu::READ_CMD, 2); + insertInCommandAndReplyMap(plpcdu::READ_TEMP_EXT, 1); + insertInCommandAndReplyMap(plpcdu::READ_WITH_TEMP_EXT, 1); insertInCommandAndReplyMap(plpcdu::SETUP_CMD, 1); } @@ -277,27 +288,31 @@ ReturnValue_t PayloadPcduHandler::interpretDeviceReply(DeviceCommandId_t id, break; } case (READ_CMD): { - PoolReadGuard pg(&adcSet); - if (pg.getReadResult() != returnvalue::OK) { - return pg.getReadResult(); + { + PoolReadGuard pg(&adcSet); + if (pg.getReadResult() != returnvalue::OK) { + return pg.getReadResult(); + } + handleExtConvRead(packet); + checkAdcValues(); + adcSet.setValidity(true, true); } - handleExtConvRead(packet); - checkAdcValues(); - adcSet.setValidity(true, true); handlePrintout(); break; } case (READ_WITH_TEMP_EXT): { - PoolReadGuard pg(&adcSet); - if (pg.getReadResult() != returnvalue::OK) { - return pg.getReadResult(); + { + PoolReadGuard pg(&adcSet); + if (pg.getReadResult() != returnvalue::OK) { + return pg.getReadResult(); + } + handleExtConvRead(packet); + uint8_t tempStartIdx = ADC_REPLY_SIZE + TEMP_REPLY_SIZE - 2; + adcSet.tempC.value = + max1227::getTemperature(packet[tempStartIdx] << 8 | packet[tempStartIdx + 1]); + checkAdcValues(); + adcSet.setValidity(true, true); } - handleExtConvRead(packet); - uint8_t tempStartIdx = ADC_REPLY_SIZE + TEMP_REPLY_SIZE - 2; - adcSet.tempC.value = - max1227::getTemperature(packet[tempStartIdx] << 8 | packet[tempStartIdx + 1]); - checkAdcValues(); - adcSet.setValidity(true, true); handlePrintout(); break; } @@ -367,16 +382,9 @@ void PayloadPcduHandler::enablePeriodicPrintout(bool enable, uint8_t divider) { void PayloadPcduHandler::quickTransitionBackToOff(bool startTransitionToOff, bool notifyFdir) { States currentState = state; - gpioIF->pullLow(gpioIds::PLPCDU_ENB_HPA); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_MPA); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_X8); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_DRO); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT0); - gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT1); + pullAllGpiosLow(200); state = States::STACK_5V_SWITCHING; - adcState = AdcStates::OFF; + adcState = AdcState::OFF; if (startTransitionToOff) { startTransition(MODE_OFF, 0); } @@ -405,10 +413,13 @@ void PayloadPcduHandler::checkAdcValues() { adcSet.processed[U_DRO_DIV_6] = static_cast(adcSet.channels[11]) * SCALE_VOLTAGE; float lowerBound = 0.0; float upperBound = 0.0; - bool adcTransition = false; - adcTransition = state == States::ON_TRANS_DRO and adcCountdown.isBusy(); - // Now check against voltage and current limits, depending on state - if (state >= States::ON_TRANS_DRO and not adcTransition) { + bool adcTransition = adcState == AdcState::NORMAL and adcCountdown.isBusy(); + if (NO_ADC_CHECKS or adcTransition) { + return; + } + // Now check against voltage and current limits. + uint8_t submode = getSubmode(); + if (((submode >> NormalSubmodeBits::DRO_ON) & 0b1) == 0b1) { if (ssrToDroInjectionRequested) { handleFailureInjection("SSR to DRO", NEG_V_OUT_OF_BOUNDS); ssrToDroInjectionRequested = false; @@ -435,8 +446,7 @@ void PayloadPcduHandler::checkAdcValues() { return; } } - adcTransition = state == States::ON_TRANS_X8 and adcCountdown.isBusy(); - if (state >= States::ON_TRANS_X8 and not adcTransition) { + if (((submode >> NormalSubmodeBits::X8_ON) & 0b1) == 0b1) { if (droToX8InjectionRequested) { handleFailureInjection("X8 to TX", U_X8_OUT_OF_BOUNDS); droToX8InjectionRequested = false; @@ -453,8 +463,7 @@ void PayloadPcduHandler::checkAdcValues() { return; } } - adcTransition = state == States::ON_TRANS_TX and adcCountdown.isBusy(); - if (state >= States::ON_TRANS_TX and not adcTransition) { + if (((submode >> NormalSubmodeBits::TX_ON) & 0b1) == 0b1) { if (txToMpaInjectionRequested) { handleFailureInjection("TX to MPA", U_TX_OUT_OF_BOUNDS); txToMpaInjectionRequested = false; @@ -471,8 +480,7 @@ void PayloadPcduHandler::checkAdcValues() { return; } } - adcTransition = state == States::ON_TRANS_MPA and adcCountdown.isBusy(); - if (state >= States::ON_TRANS_MPA and not adcTransition) { + if (((submode >> NormalSubmodeBits::MPA_ON) & 0b1) == 0b1) { if (mpaToHpaInjectionRequested) { handleFailureInjection("MPA to HPA", U_HPA_OUT_OF_BOUNDS); mpaToHpaInjectionRequested = false; @@ -489,8 +497,7 @@ void PayloadPcduHandler::checkAdcValues() { return; } } - adcTransition = state == States::ON_TRANS_HPA and adcCountdown.isBusy(); - if (state >= States::ON_TRANS_HPA and not adcTransition) { + if (((submode >> NormalSubmodeBits::HPA_ON) & 0b1) == 0b1) { if (allOnInjectRequested) { handleFailureInjection("All On", U_HPA_OUT_OF_BOUNDS); allOnInjectRequested = false; @@ -677,6 +684,18 @@ void PayloadPcduHandler::handleFailureInjection(std::string output, Event event) droToX8InjectionRequested = false; } +void PayloadPcduHandler::pullAllGpiosLow(uint32_t delayBeforeSwitchingOffDro) { + sif::info << "Pulling all PL PCDU GPIOs to low" << std::endl; + gpioIF->pullLow(gpioIds::PLPCDU_ENB_HPA); + gpioIF->pullLow(gpioIds::PLPCDU_ENB_MPA); + gpioIF->pullLow(gpioIds::PLPCDU_ENB_TX); + gpioIF->pullLow(gpioIds::PLPCDU_ENB_X8); + TaskFactory::delayTask(delayBeforeSwitchingOffDro); + gpioIF->pullLow(gpioIds::PLPCDU_ENB_DRO); + gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT0); + gpioIF->pullLow(gpioIds::PLPCDU_ENB_VBAT1); +} + ReturnValue_t PayloadPcduHandler::handleDoubleParamUpdate(std::string key, ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues) { @@ -692,6 +711,8 @@ ReturnValue_t PayloadPcduHandler::handleDoubleParamUpdate(std::string key, return params.writeJsonFile(); } +LocalPoolDataSetBase* PayloadPcduHandler::getDataSetHandle(sid_t sid) { return &adcSet; } + #ifdef XIPHOS_Q7S ReturnValue_t PayloadPcduHandler::extConvAsTwoCallback(SpiComIF* comIf, SpiCookie* cookie, const uint8_t* sendData, size_t sendLen, diff --git a/mission/payload/PayloadPcduHandler.h b/mission/payload/PayloadPcduHandler.h index b73de353..d9370dcf 100644 --- a/mission/payload/PayloadPcduHandler.h +++ b/mission/payload/PayloadPcduHandler.h @@ -76,6 +76,8 @@ class PayloadPcduHandler : public DeviceHandlerBase { #endif private: + static constexpr bool NO_ADC_CHECKS = false; + enum class States : uint8_t { PL_PCDU_OFF, STACK_5V_SWITCHING, @@ -84,20 +86,7 @@ class PayloadPcduHandler : public DeviceHandlerBase { // Solid State Relay, enable battery voltages VBAT0 and VBAT1. This will also switch on // the ADC ON_TRANS_SSR, - ON_TRANS_ADC_CLOSE_ZERO, - // Enable Dielectric Resonant Oscillator and start monitoring voltages as - // soon as DRO voltage reaches 6V - ON_TRANS_DRO, - // Switch on X8 compoennt and monitor voltages for 5 seconds - ON_TRANS_X8, - // Switch on TX component and monitor voltages for 5 seconds - ON_TRANS_TX, - // Switch on MPA component and monitor voltages for 5 seconds - ON_TRANS_MPA, - // Switch on HPA component and monitor voltages for 5 seconds - ON_TRANS_HPA, - // All components of the experiment are on - PL_PCDU_ON, + ON_TRANS_ADC_CLOSE_ZERO } state = States::PL_PCDU_OFF; duallane::Submodes pwrSubmode = duallane::Submodes::A_SIDE; @@ -106,7 +95,7 @@ class PayloadPcduHandler : public DeviceHandlerBase { enum class MonitoringMode { NONE, CLOSE_TO_ZERO, NEGATIVE } monMode = MonitoringMode::NONE; - enum class AdcStates { OFF, BOOT_DELAY, SEND_SETUP, NORMAL } adcState = AdcStates::OFF; + enum class AdcState { OFF, BOOT_DELAY, SEND_SETUP, NORMAL } adcState = AdcState::OFF; bool goToNormalMode = false; plpcdu::PlPcduAdcSet adcSet; @@ -128,6 +117,7 @@ class PayloadPcduHandler : public DeviceHandlerBase { bool mpaToHpaInjectionRequested = false; bool allOnInjectRequested = false; bool clearSetOnOffFlag = true; + bool toNormalOneShot = true; PeriodicOperationDivider opDivider = PeriodicOperationDivider(5); uint8_t tempReadDivisor = 1; @@ -168,6 +158,7 @@ class PayloadPcduHandler : public DeviceHandlerBase { void handleExtConvRead(const uint8_t* bufStart); void handlePrintout(); + void pullAllGpiosLow(uint32_t delayBeforeSwitchingOffDro); void checkAdcValues(); void handleOutOfBoundsPrintout(); void checkJsonFileInit(); @@ -178,6 +169,7 @@ class PayloadPcduHandler : public DeviceHandlerBase { ReturnValue_t serializeFloat(uint32_t& param, float val); ReturnValue_t handleDoubleParamUpdate(std::string key, ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues); + LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override; }; #endif /* LINUX_DEVICES_PLPCDUHANDLER_H_ */ diff --git a/mission/payload/RadiationSensorHandler.cpp b/mission/payload/RadiationSensorHandler.cpp index d18fcb2c..07363fba 100644 --- a/mission/payload/RadiationSensorHandler.cpp +++ b/mission/payload/RadiationSensorHandler.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,8 @@ RadiationSensorHandler::RadiationSensorHandler(object_id_t objectId, object_id_t if (comCookie == nullptr) { sif::error << "RadiationSensorHandler: Invalid com cookie" << std::endl; } + // Time out immediately so we get an immediate measurement at device startup. + measurementCd.timeOut(); } RadiationSensorHandler::~RadiationSensorHandler() {} @@ -51,14 +54,17 @@ void RadiationSensorHandler::doShutDown() { } ReturnValue_t RadiationSensorHandler::buildNormalDeviceCommand(DeviceCommandId_t *id) { + if (measurementCd.isBusy()) { + return NOTHING_TO_SEND; + } switch (communicationStep) { case CommunicationStep::START_CONVERSION: { - *id = RAD_SENSOR::START_CONVERSION; + *id = radSens::START_CONVERSION; communicationStep = CommunicationStep::READ_CONVERSIONS; break; } case CommunicationStep::READ_CONVERSIONS: { - *id = RAD_SENSOR::READ_CONVERSIONS; + *id = radSens::READ_CONVERSIONS; communicationStep = CommunicationStep::START_CONVERSION; break; } @@ -73,7 +79,7 @@ ReturnValue_t RadiationSensorHandler::buildNormalDeviceCommand(DeviceCommandId_t ReturnValue_t RadiationSensorHandler::buildTransitionDeviceCommand(DeviceCommandId_t *id) { if (internalState == InternalState::SETUP) { - *id = RAD_SENSOR::WRITE_SETUP; + *id = radSens::WRITE_SETUP; } else { return NOTHING_TO_SEND; } @@ -84,15 +90,19 @@ ReturnValue_t RadiationSensorHandler::buildCommandFromCommand(DeviceCommandId_t const uint8_t *commandData, size_t commandDataLen) { switch (deviceCommand) { - case (RAD_SENSOR::WRITE_SETUP): { - cmdBuffer[0] = RAD_SENSOR::SETUP_DEFINITION; + case (radSens::WRITE_SETUP): { + cmdBuffer[0] = radSens::SETUP_DEFINITION; rawPacket = cmdBuffer; rawPacketLen = 1; internalState = InternalState::CONFIGURED; return returnvalue::OK; } - case (RAD_SENSOR::START_CONVERSION): { + case (radSens::START_CONVERSION): { ReturnValue_t result = gpioIF->pullHigh(gpioIds::ENABLE_RADFET); + // Test a small delay between pulling the RADFET high and reading the sensor. As long as this + // delay remains small enough, this should not cause scheduling issues. Do not make this + // delay large, this device might be scheduled inside the ACS PST! + TaskFactory::delayTask(5); if (result != returnvalue::OK) { #if OBSW_VERBOSE_LEVEL >= 1 sif::warning @@ -102,25 +112,25 @@ ReturnValue_t RadiationSensorHandler::buildCommandFromCommand(DeviceCommandId_t #endif } /* First the fifo will be reset here */ - cmdBuffer[0] = RAD_SENSOR::RESET_DEFINITION; - cmdBuffer[1] = RAD_SENSOR::CONVERSION_DEFINITION; + cmdBuffer[0] = radSens::RESET_DEFINITION; + cmdBuffer[1] = radSens::CONVERSION_DEFINITION; rawPacket = cmdBuffer; rawPacketLen = 2; return returnvalue::OK; } - case (RAD_SENSOR::READ_CONVERSIONS): { - cmdBuffer[0] = RAD_SENSOR::DUMMY_BYTE; - std::memset(cmdBuffer, RAD_SENSOR::DUMMY_BYTE, RAD_SENSOR::READ_SIZE); + case (radSens::READ_CONVERSIONS): { + cmdBuffer[0] = radSens::DUMMY_BYTE; + std::memset(cmdBuffer, radSens::DUMMY_BYTE, radSens::READ_SIZE); rawPacket = cmdBuffer; - rawPacketLen = RAD_SENSOR::READ_SIZE; + rawPacketLen = radSens::READ_SIZE; return returnvalue::OK; } - case RAD_SENSOR::ENABLE_DEBUG_OUTPUT: { + case radSens::ENABLE_DEBUG_OUTPUT: { printPeriodicData = true; rawPacketLen = 0; return returnvalue::OK; } - case RAD_SENSOR::DISABLE_DEBUG_OUTPUT: { + case radSens::DISABLE_DEBUG_OUTPUT: { rawPacketLen = 0; printPeriodicData = false; return returnvalue::OK; @@ -132,12 +142,11 @@ ReturnValue_t RadiationSensorHandler::buildCommandFromCommand(DeviceCommandId_t } void RadiationSensorHandler::fillCommandAndReplyMap() { - this->insertInCommandMap(RAD_SENSOR::WRITE_SETUP); - this->insertInCommandMap(RAD_SENSOR::START_CONVERSION); - this->insertInCommandMap(RAD_SENSOR::ENABLE_DEBUG_OUTPUT); - this->insertInCommandMap(RAD_SENSOR::DISABLE_DEBUG_OUTPUT); - this->insertInCommandAndReplyMap(RAD_SENSOR::READ_CONVERSIONS, 1, &dataset, - RAD_SENSOR::READ_SIZE); + this->insertInCommandMap(radSens::WRITE_SETUP); + this->insertInCommandMap(radSens::START_CONVERSION); + this->insertInCommandMap(radSens::ENABLE_DEBUG_OUTPUT); + this->insertInCommandMap(radSens::DISABLE_DEBUG_OUTPUT); + this->insertInCommandAndReplyMap(radSens::READ_CONVERSIONS, 1, &dataset, radSens::READ_SIZE); } ReturnValue_t RadiationSensorHandler::scanForReply(const uint8_t *start, size_t remainingSize, @@ -145,11 +154,11 @@ ReturnValue_t RadiationSensorHandler::scanForReply(const uint8_t *start, size_t *foundId = this->getPendingCommand(); switch (*foundId) { - case RAD_SENSOR::START_CONVERSION: - case RAD_SENSOR::WRITE_SETUP: + case radSens::START_CONVERSION: + case radSens::WRITE_SETUP: *foundLen = remainingSize; return IGNORE_REPLY_DATA; - case RAD_SENSOR::READ_CONVERSIONS: { + case radSens::READ_CONVERSIONS: { ReturnValue_t result = gpioIF->pullLow(gpioIds::ENABLE_RADFET); if (result != returnvalue::OK) { #if OBSW_VERBOSE_LEVEL >= 1 @@ -160,8 +169,8 @@ ReturnValue_t RadiationSensorHandler::scanForReply(const uint8_t *start, size_t } break; } - case RAD_SENSOR::ENABLE_DEBUG_OUTPUT: - case RAD_SENSOR::DISABLE_DEBUG_OUTPUT: + case radSens::ENABLE_DEBUG_OUTPUT: + case radSens::DISABLE_DEBUG_OUTPUT: sif::info << "RadiationSensorHandler::scanForReply: " << remainingSize << std::endl; break; default: @@ -176,24 +185,27 @@ ReturnValue_t RadiationSensorHandler::scanForReply(const uint8_t *start, size_t ReturnValue_t RadiationSensorHandler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) { switch (id) { - case RAD_SENSOR::READ_CONVERSIONS: { + case radSens::READ_CONVERSIONS: { uint8_t offset = 0; - PoolReadGuard readSet(&dataset); - uint16_t tempRaw = ((packet[offset] & 0x0f) << 8) | packet[offset + 1]; - dataset.temperatureCelcius = max1227::getTemperature(tempRaw); - offset += 2; - dataset.ain0 = (*(packet + offset) << 8) | *(packet + offset + 1); - offset += 2; - dataset.ain1 = (*(packet + offset) << 8) | *(packet + offset + 1); - offset += 6; - dataset.ain4 = (*(packet + offset) << 8) | *(packet + offset + 1); - offset += 2; - dataset.ain5 = (*(packet + offset) << 8) | *(packet + offset + 1); - offset += 2; - dataset.ain6 = (*(packet + offset) << 8) | *(packet + offset + 1); - offset += 2; - dataset.ain7 = (*(packet + offset) << 8) | *(packet + offset + 1); - dataset.setValidity(true, true); + measurementCd.resetTimer(); + { + PoolReadGuard readSet(&dataset); + uint16_t tempRaw = ((packet[offset] & 0x0f) << 8) | packet[offset + 1]; + dataset.temperatureCelcius = max1227::getTemperature(tempRaw); + offset += 2; + dataset.ain0 = (*(packet + offset) << 8) | *(packet + offset + 1); + offset += 2; + dataset.ain1 = (*(packet + offset) << 8) | *(packet + offset + 1); + offset += 6; + dataset.ain4 = (*(packet + offset) << 8) | *(packet + offset + 1); + offset += 2; + dataset.ain5 = (*(packet + offset) << 8) | *(packet + offset + 1); + offset += 2; + dataset.ain6 = (*(packet + offset) << 8) | *(packet + offset + 1); + offset += 2; + dataset.ain7 = (*(packet + offset) << 8) | *(packet + offset + 1); + dataset.setValidity(true, true); + } if (printPeriodicData) { sif::info << "Radiation sensor temperature: " << dataset.temperatureCelcius << " °C" << std::dec << std::endl; @@ -204,6 +216,12 @@ ReturnValue_t RadiationSensorHandler::interpretDeviceReply(DeviceCommandId_t id, sif::info << "Radiation sensor ADC value channel 6: " << dataset.ain6 << std::endl; sif::info << "Radiation sensor ADC value channel 7: " << dataset.ain7 << std::endl; } + ReturnValue_t result = + getHkManagerHandle()->generateHousekeepingPacket(dataset.getSid(), &dataset, true); + if (result != returnvalue::OK) { + // TODO: Maybe add event? + sif::error << "Generating HK set for radiation sensor failed" << std::endl; + } break; } default: { @@ -220,15 +238,18 @@ uint32_t RadiationSensorHandler::getTransitionDelayMs(Mode_t modeFrom, Mode_t mo ReturnValue_t RadiationSensorHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) { - localDataPoolMap.emplace(RAD_SENSOR::TEMPERATURE_C, new PoolEntry({0.0})); - localDataPoolMap.emplace(RAD_SENSOR::AIN0, new PoolEntry({0})); - localDataPoolMap.emplace(RAD_SENSOR::AIN1, new PoolEntry({0})); - localDataPoolMap.emplace(RAD_SENSOR::AIN4, new PoolEntry({0})); - localDataPoolMap.emplace(RAD_SENSOR::AIN5, new PoolEntry({0})); - localDataPoolMap.emplace(RAD_SENSOR::AIN6, new PoolEntry({0})); - localDataPoolMap.emplace(RAD_SENSOR::AIN7, new PoolEntry({0})); + localDataPoolMap.emplace(radSens::TEMPERATURE_C, new PoolEntry({0.0})); + localDataPoolMap.emplace(radSens::AIN0, new PoolEntry({0})); + localDataPoolMap.emplace(radSens::AIN1, new PoolEntry({0})); + localDataPoolMap.emplace(radSens::AIN4, new PoolEntry({0})); + localDataPoolMap.emplace(radSens::AIN5, new PoolEntry({0})); + localDataPoolMap.emplace(radSens::AIN6, new PoolEntry({0})); + localDataPoolMap.emplace(radSens::AIN7, new PoolEntry({0})); + // It should normally not be necessary to enable this set, as a sample TM will be generated + // after a measurement. If this is still enabled, sample with double the measurement frequency + // to ensure we get all measurements. poolManager.subscribeForRegularPeriodicPacket( - subdp::RegularHkPeriodicParams(dataset.getSid(), false, 20.0)); + subdp::RegularHkPeriodicParams(dataset.getSid(), false, DEFAULT_MEASUREMENT_CD_MS / 2)); return returnvalue::OK; } diff --git a/mission/payload/RadiationSensorHandler.h b/mission/payload/RadiationSensorHandler.h index de9bcc93..429cda79 100644 --- a/mission/payload/RadiationSensorHandler.h +++ b/mission/payload/RadiationSensorHandler.h @@ -38,13 +38,16 @@ class RadiationSensorHandler : public DeviceHandlerBase { LocalDataPoolManager &poolManager) override; private: + static constexpr uint32_t DEFAULT_MEASUREMENT_CD_MS = 30 * 60 * 1000; + enum class CommunicationStep { START_CONVERSION, READ_CONVERSIONS }; enum class InternalState { OFF, POWER_SWITCHING, SETUP, CONFIGURED }; bool printPeriodicData = false; - RAD_SENSOR::RadSensorDataset dataset; - static const uint8_t MAX_CMD_LEN = RAD_SENSOR::READ_SIZE; + radSens::RadSensorDataset dataset; + Countdown measurementCd = Countdown(DEFAULT_MEASUREMENT_CD_MS); + static const uint8_t MAX_CMD_LEN = radSens::READ_SIZE; GpioIF *gpioIF = nullptr; Stack5VHandler &stackHandler; diff --git a/mission/payload/ScexDeviceHandler.cpp b/mission/payload/ScexDeviceHandler.cpp index 8c3b7386..12c62f7a 100644 --- a/mission/payload/ScexDeviceHandler.cpp +++ b/mission/payload/ScexDeviceHandler.cpp @@ -19,11 +19,16 @@ using namespace returnvalue; ScexDeviceHandler::ScexDeviceHandler(object_id_t objectId, ScexUartReader& reader, CookieIF* cookie, SdCardMountedIF& sdcMan) - : DeviceHandlerBase(objectId, reader.getObjectId(), cookie), sdcMan(sdcMan), reader(reader) {} + : DeviceHandlerBase(objectId, reader.getObjectId(), cookie), sdcMan(sdcMan), reader(reader) { + fsUnusableEventCd.timeOut(); +} ScexDeviceHandler::~ScexDeviceHandler() {} -void ScexDeviceHandler::doStartUp() { setMode(MODE_ON); } +void ScexDeviceHandler::doStartUp() { + filesystemChecks(); + setMode(MODE_ON); +} void ScexDeviceHandler::doShutDown() { reader.reset(); @@ -47,7 +52,7 @@ ReturnValue_t ScexDeviceHandler::buildCommandFromCommand(DeviceCommandId_t devic return DeviceHandlerIF::COMMAND_NOT_IMPLEMENTED; } bool tempCheck = false; - if (commandDataLen == 1) { + if (commandDataLen >= 1) { tempCheck = commandData[0]; } if (commandActive) { @@ -215,8 +220,12 @@ ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, cons } fileNameSet = true; } else { - ofstream out(fileName, - ofstream::binary | ofstream::app); // append + if (!sdcMan.isSdCardUsable(std::nullopt)) { + fsUnsableEvent(); + return returnvalue::FAILED; + } + // Append to existing file. + ofstream out(fileName, ofstream::binary | ofstream::app); if (out.bad()) { sif::error << "ScexDeviceHandler::handleValidReply: Could not open file " << fileName << std::endl; @@ -280,18 +289,8 @@ ReturnValue_t ScexDeviceHandler::interpretDeviceReply(DeviceCommandId_t id, cons } void ScexDeviceHandler::performOperationHook() { - auto mntPrefix = sdcMan.getCurrentMountPrefix(); - if (mntPrefix != nullptr) { - std::filesystem::path fullFilePath = mntPrefix; - std::error_code e; - fullFilePath /= "scex"; - bool fileExists = std::filesystem::exists(fullFilePath, e); - if (not fileExists) { - bool created = std::filesystem::create_directory(fullFilePath, e); - if (not created) { - sif::error << "Could not create SCEX directory: " << e << std::endl; - } - } + if (getMode() != MODE_OFF) { + filesystemChecks(); } uint32_t remainingMillis = finishCountdown.getRemainingMillis(); if (commandActive and finishCountdown.hasTimedOut()) { @@ -319,6 +318,33 @@ ReturnValue_t ScexDeviceHandler::initializeLocalDataPool(localpool::DataPool& lo return OK; } +void ScexDeviceHandler::filesystemChecks() { + auto mntPrefix = sdcMan.getCurrentMountPrefix(); + if (mntPrefix == nullptr or !sdcMan.isSdCardUsable(std::nullopt)) { + sif::warning << "SCEX: Filesystem currently unavailable" << std::endl; + fsUnsableEvent(); + } else { + std::filesystem::path fullFilePath = mntPrefix; + std::error_code e; + fullFilePath /= "scex"; + bool fileExists = std::filesystem::exists(fullFilePath, e); + if (not fileExists) { + bool created = std::filesystem::create_directory(fullFilePath, e); + if (not created) { + sif::error << "Could not create SCEX directory: " << e << std::endl; + } + } + } +} + +void ScexDeviceHandler::fsUnsableEvent() { + if (fsUnusableEventCd.isBusy()) { + return; + } + triggerEvent(scex::FS_UNUSABLE); + fsUnusableEventCd.resetTimer(); +} + ReturnValue_t ScexDeviceHandler::generateNewScexFile(const char* cmdName) { char timeString[64]{}; auto activeSd = sdcMan.getActiveSdCard(); @@ -328,7 +354,8 @@ ReturnValue_t ScexDeviceHandler::generateNewScexFile(const char* cmdName) { std::ostringstream oss; auto prefix = sdcMan.getCurrentMountPrefix(); - if (prefix == nullptr) { + if (prefix == nullptr or !sdcMan.isSdCardUsable(std::nullopt)) { + fsUnsableEvent(); return returnvalue::FAILED; } timeval tv; diff --git a/mission/payload/ScexDeviceHandler.h b/mission/payload/ScexDeviceHandler.h index e7721ef6..ecde12a5 100644 --- a/mission/payload/ScexDeviceHandler.h +++ b/mission/payload/ScexDeviceHandler.h @@ -42,6 +42,7 @@ class ScexDeviceHandler : public DeviceHandlerBase { scex::Cmds currCmd = scex::Cmds::PING; SdCardMountedIF &sdcMan; Countdown finishCountdown = Countdown(LONG_CD); + Countdown fsUnusableEventCd = Countdown(10000); // DeviceHandlerBase private function implementation void doStartUp() override; @@ -49,12 +50,15 @@ class ScexDeviceHandler : public DeviceHandlerBase { ScexHelper helper; ScexUartReader &reader; + void fsUnsableEvent(); + void performOperationHook() override; ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t *id) override; ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t *id) override; ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, const uint8_t *commandData, size_t commandDataLen) override; void fillCommandAndReplyMap() override; + void filesystemChecks(); ReturnValue_t scanForReply(const uint8_t *start, size_t remainingSize, DeviceCommandId_t *foundId, size_t *foundLen) override; ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) override; diff --git a/mission/payload/radSensorDefinitions.h b/mission/payload/radSensorDefinitions.h index 8f6b9f0b..3262c23c 100644 --- a/mission/payload/radSensorDefinitions.h +++ b/mission/payload/radSensorDefinitions.h @@ -1,7 +1,7 @@ #ifndef MISSION_DEVICES_DEVICEDEFINITIONS_RADSENSOR_H_ #define MISSION_DEVICES_DEVICEDEFINITIONS_RADSENSOR_H_ -namespace RAD_SENSOR { +namespace radSens { static const DeviceCommandId_t NONE = 0x0; // Set when no command is pending @@ -78,6 +78,6 @@ class RadSensorDataset : public StaticLocalDataSet { lp_var_t ain6 = lp_var_t(sid.objectId, AIN6, this); lp_var_t ain7 = lp_var_t(sid.objectId, AIN7, this); }; -} // namespace RAD_SENSOR +} // namespace radSens #endif /* MISSION_DEVICES_DEVICEDEFINITIONS_RADSENSOR_H_ */ diff --git a/mission/payload/scexHelpers.h b/mission/payload/scexHelpers.h index aa0f320b..06a8bb07 100644 --- a/mission/payload/scexHelpers.h +++ b/mission/payload/scexHelpers.h @@ -20,6 +20,7 @@ static constexpr Event EXPERIMENT_TIMEDOUT = event::makeEvent(SUBSYSTEM_ID, 1, s //! FRAM, One Cell or All cells command finished. P1: Command ID static constexpr Event MULTI_PACKET_COMMAND_DONE = event::makeEvent(SUBSYSTEM_ID, 2, severity::INFO); +static constexpr Event FS_UNUSABLE = event::makeEvent(SUBSYSTEM_ID, 3, severity::LOW); enum Cmds : DeviceCommandId_t { PING = 0b00111, diff --git a/mission/pollingSeqTables.cpp b/mission/pollingSeqTables.cpp index 0367ef27..4896d8db 100644 --- a/mission/pollingSeqTables.cpp +++ b/mission/pollingSeqTables.cpp @@ -39,56 +39,67 @@ ReturnValue_t pst::pstSyrlinks(FixedTimeslotTaskIF *thisSequence) { // I don't think this needs to be in a PST because linux takes care of bus serialization, but // keep it like this for now, it works -ReturnValue_t pst::pstI2cProcessingSystem(FixedTimeslotTaskIF *thisSequence) { +ReturnValue_t pst::pstI2c(TmpSchedConfig schedConf, FixedTimeslotTaskIF *thisSequence) { // Length of a communication cycle uint32_t length = thisSequence->getPeriodMs(); static_cast(length); - // These are actually part of another bus, but this works, so keep it like this for now - thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_0, length * 0.2, - DeviceHandlerIF::PERFORM_OPERATION); - thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_0, length * 0.2, DeviceHandlerIF::SEND_WRITE); - thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_0, length * 0.2, DeviceHandlerIF::GET_WRITE); - thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_0, length * 0.3, DeviceHandlerIF::SEND_READ); - thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_0, length * 0.3, DeviceHandlerIF::GET_READ); + if (schedConf.scheduleTmpDev0) { + thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_0, length * 0, + DeviceHandlerIF::PERFORM_OPERATION); + thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_0, length * 0, DeviceHandlerIF::SEND_WRITE); + thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_0, length * 0.1, DeviceHandlerIF::GET_WRITE); + thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_0, length * 0.1, DeviceHandlerIF::SEND_READ); + thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_0, length * 0.1, DeviceHandlerIF::GET_READ); + } - thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_1, length * 0.4, - DeviceHandlerIF::PERFORM_OPERATION); - thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_1, length * 0.4, DeviceHandlerIF::SEND_WRITE); - thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_1, length * 0.4, DeviceHandlerIF::GET_WRITE); - thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_1, length * 0.5, DeviceHandlerIF::SEND_READ); - thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_1, length * 0.5, DeviceHandlerIF::GET_READ); + if (schedConf.scheduleTmpDev1) { + thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_1, length * 0.2, + DeviceHandlerIF::PERFORM_OPERATION); + thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_1, length * 0.2, + DeviceHandlerIF::SEND_WRITE); + thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_1, length * 0.2, DeviceHandlerIF::GET_WRITE); + thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_1, length * 0.3, DeviceHandlerIF::SEND_READ); + thisSequence->addSlot(objects::TMP1075_HANDLER_TCS_1, length * 0.3, DeviceHandlerIF::GET_READ); + } - thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_0, length * 0.6, - DeviceHandlerIF::PERFORM_OPERATION); - thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_0, length * 0.6, - DeviceHandlerIF::SEND_WRITE); - thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_0, length * 0.6, - DeviceHandlerIF::GET_WRITE); - thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_0, length * 0.7, - DeviceHandlerIF::SEND_READ); - thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_0, length * 0.7, DeviceHandlerIF::GET_READ); - // damaged - /* - thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_1, length * 0.4, - DeviceHandlerIF::PERFORM_OPERATION); - thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_1, length * 0.4, - DeviceHandlerIF::SEND_WRITE); - thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_1, length * 0.4, - DeviceHandlerIF::GET_WRITE); - thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_1, length * 0.4, - DeviceHandlerIF::SEND_READ); - thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_1, length * 0.4, DeviceHandlerIF::GET_READ); - */ - thisSequence->addSlot(objects::TMP1075_HANDLER_IF_BOARD, length * 0.8, - DeviceHandlerIF::PERFORM_OPERATION); - thisSequence->addSlot(objects::TMP1075_HANDLER_IF_BOARD, length * 0.8, - DeviceHandlerIF::SEND_WRITE); - thisSequence->addSlot(objects::TMP1075_HANDLER_IF_BOARD, length * 0.8, - DeviceHandlerIF::GET_WRITE); - thisSequence->addSlot(objects::TMP1075_HANDLER_IF_BOARD, length * 0.9, - DeviceHandlerIF::SEND_READ); - thisSequence->addSlot(objects::TMP1075_HANDLER_IF_BOARD, length * 0.9, DeviceHandlerIF::GET_READ); + if (schedConf.schedulePlPcduDev0) { + thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_0, length * 0.4, + DeviceHandlerIF::PERFORM_OPERATION); + thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_0, length * 0.4, + DeviceHandlerIF::SEND_WRITE); + thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_0, length * 0.4, + DeviceHandlerIF::GET_WRITE); + thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_0, length * 0.5, + DeviceHandlerIF::SEND_READ); + thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_0, length * 0.5, + DeviceHandlerIF::GET_READ); + } + if (schedConf.schedulePlPcduDev1) { + thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_1, length * 0.6, + DeviceHandlerIF::PERFORM_OPERATION); + thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_1, length * 0.6, + DeviceHandlerIF::SEND_WRITE); + thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_1, length * 0.6, + DeviceHandlerIF::GET_WRITE); + thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_1, length * 0.7, + DeviceHandlerIF::SEND_READ); + thisSequence->addSlot(objects::TMP1075_HANDLER_PLPCDU_1, length * 0.7, + DeviceHandlerIF::GET_READ); + } + + if (schedConf.scheduleIfBoardDev) { + thisSequence->addSlot(objects::TMP1075_HANDLER_IF_BOARD, length * 0.8, + DeviceHandlerIF::PERFORM_OPERATION); + thisSequence->addSlot(objects::TMP1075_HANDLER_IF_BOARD, length * 0.8, + DeviceHandlerIF::SEND_WRITE); + thisSequence->addSlot(objects::TMP1075_HANDLER_IF_BOARD, length * 0.8, + DeviceHandlerIF::GET_WRITE); + thisSequence->addSlot(objects::TMP1075_HANDLER_IF_BOARD, length * 0.9, + DeviceHandlerIF::SEND_READ); + thisSequence->addSlot(objects::TMP1075_HANDLER_IF_BOARD, length * 0.9, + DeviceHandlerIF::GET_READ); + } static_cast(length); return thisSequence->checkSequence(); } @@ -580,7 +591,6 @@ ReturnValue_t pst::pstTcsAndAcs(FixedTimeslotTaskIF *thisSequence, AcsPstCfg cfg thisSequence->addSlot(objects::BPX_BATT_HANDLER, length * config::spiSched::SCHED_BLOCK_9_PERIOD, DeviceHandlerIF::GET_READ); -#if OBSW_ADD_RAD_SENSORS == 1 /* Radiation sensor */ thisSequence->addSlot(objects::RAD_SENSOR, length * config::spiSched::SCHED_BLOCK_9_PERIOD, DeviceHandlerIF::PERFORM_OPERATION); @@ -592,6 +602,5 @@ ReturnValue_t pst::pstTcsAndAcs(FixedTimeslotTaskIF *thisSequence, AcsPstCfg cfg DeviceHandlerIF::SEND_READ); thisSequence->addSlot(objects::RAD_SENSOR, length * config::spiSched::SCHED_BLOCK_9_PERIOD, DeviceHandlerIF::GET_READ); -#endif return returnvalue::OK; } diff --git a/mission/pollingSeqTables.h b/mission/pollingSeqTables.h index e3bc0ad0..5df3c954 100644 --- a/mission/pollingSeqTables.h +++ b/mission/pollingSeqTables.h @@ -39,6 +39,16 @@ struct AcsPstCfg { bool scheduleStr = true; }; +// Default config is for FM. +struct TmpSchedConfig { + bool scheduleTmpDev0 = true; + bool scheduleTmpDev1 = true; + bool schedulePlPcduDev0 = true; + // damaged on FM + bool schedulePlPcduDev1 = false; + bool scheduleIfBoardDev = true; +}; + /** * @brief This function creates the PST for all gomspace devices. * @details @@ -51,7 +61,7 @@ ReturnValue_t pstSyrlinks(FixedTimeslotTaskIF* thisSequence); ReturnValue_t pstTcsAndAcs(FixedTimeslotTaskIF* thisSequence, AcsPstCfg cfg); -ReturnValue_t pstI2cProcessingSystem(FixedTimeslotTaskIF* thisSequence); +ReturnValue_t pstI2c(TmpSchedConfig schedConf, FixedTimeslotTaskIF* thisSequence); /** * Generic test PST diff --git a/mission/power/BpxBatteryHandler.cpp b/mission/power/BpxBatteryHandler.cpp index b4aece40..96253705 100644 --- a/mission/power/BpxBatteryHandler.cpp +++ b/mission/power/BpxBatteryHandler.cpp @@ -1,4 +1,5 @@ #include +#include #include BpxBatteryHandler::BpxBatteryHandler(object_id_t objectId, object_id_t comIF, CookieIF* comCookie, @@ -51,6 +52,9 @@ void BpxBatteryHandler::fillCommandAndReplyMap() { insertInCommandAndReplyMap(bpxBat::RESET_COUNTERS, 1, nullptr, EMPTY_REPLY_LEN); insertInCommandAndReplyMap(bpxBat::CONFIG_CMD, 1, nullptr, EMPTY_REPLY_LEN); insertInCommandAndReplyMap(bpxBat::CONFIG_GET, 1, &cfgSet, CONFIG_GET_REPLY_LEN); + insertInCommandAndReplyMap(bpxBat::CONFIG_SET, 1, nullptr, EMPTY_REPLY_LEN); + insertInCommandAndReplyMap(bpxBat::MAN_HEAT_ON, 1, nullptr, MAN_HEAT_REPLY_LEN); + insertInCommandAndReplyMap(bpxBat::MAN_HEAT_OFF, 1, nullptr, MAN_HEAT_REPLY_LEN); } ReturnValue_t BpxBatteryHandler::buildCommandFromCommand(DeviceCommandId_t deviceCommand, @@ -155,7 +159,7 @@ ReturnValue_t BpxBatteryHandler::scanForReply(const uint8_t* start, size_t remai case (bpxBat::PING): case (bpxBat::MAN_HEAT_ON): case (bpxBat::MAN_HEAT_OFF): { - if (remainingSize != PING_REPLY_LEN) { + if (remainingSize != MAN_HEAT_REPLY_LEN) { return DeviceHandlerIF::LENGTH_MISSMATCH; } break; diff --git a/mission/power/P60DockHandler.cpp b/mission/power/P60DockHandler.cpp index d643aef1..77fd9aaf 100644 --- a/mission/power/P60DockHandler.cpp +++ b/mission/power/P60DockHandler.cpp @@ -66,7 +66,7 @@ void P60DockHandler::parseHkTableReply(const uint8_t *packet) { } coreHk.battMode = newBattMode; - auxHk.heaterOn = *(packet + 0x57); + auxHk.heaterForBp4PackOn = *(packet + 0x57); auxHk.converter5VStatus = *(packet + 0x58); for (uint8_t idx = 0; idx < hk::CHNLS_LEN; idx++) { @@ -111,6 +111,8 @@ void P60DockHandler::parseHkTableReply(const uint8_t *packet) { } coreHk.setValidity(true, true); auxHk.setValidity(true, true); + // No BP4 pack, no this is always invalid. + auxHk.heaterForBp4PackOn.setValid(false); } ReturnValue_t P60DockHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, diff --git a/mission/power/bpxBattDefs.h b/mission/power/bpxBattDefs.h index 6df87efd..0e167f07 100644 --- a/mission/power/bpxBattDefs.h +++ b/mission/power/bpxBattDefs.h @@ -48,6 +48,7 @@ static constexpr uint32_t CFG_SET_ID = CONFIG_GET; static constexpr size_t GET_HK_REPLY_LEN = 23; static constexpr size_t PING_REPLY_LEN = 3; static constexpr size_t EMPTY_REPLY_LEN = 2; +static constexpr size_t MAN_HEAT_REPLY_LEN = 3; static constexpr size_t CONFIG_GET_REPLY_LEN = 5; static constexpr uint8_t PORT_PING = 1; @@ -219,6 +220,7 @@ class BpxBatteryCfg : public StaticLocalDataSet { if (size < 3) { return SerializeIF::STREAM_TOO_SHORT; } + battheatermode.value = data[0]; battheaterLow.value = data[1]; battheaterHigh.value = data[2]; diff --git a/mission/power/gsDefs.h b/mission/power/gsDefs.h index 00bdf589..1ad000d1 100644 --- a/mission/power/gsDefs.h +++ b/mission/power/gsDefs.h @@ -260,7 +260,8 @@ class HkTableDataset : public StaticLocalDataSet<32> { lp_var_t resetcause = lp_var_t(sid.objectId, pool::P60DOCK_RESETCAUSE, this); /** Battery heater control only possible on BP4 packs */ - lp_var_t heaterOn = lp_var_t(sid.objectId, pool::P60DOCK_HEATER_ON, this); + lp_var_t heaterForBp4PackOn = + lp_var_t(sid.objectId, pool::P60DOCK_HEATER_ON, this); lp_var_t converter5VStatus = lp_var_t(sid.objectId, pool::P60DOCK_CONV_5V_ENABLE_STATUS, this); diff --git a/mission/sysDefs.h b/mission/sysDefs.h index c84c237f..4a53a76d 100644 --- a/mission/sysDefs.h +++ b/mission/sysDefs.h @@ -34,6 +34,9 @@ enum Copy : int { COPY_0, COPY_1, NO_COPY, SELF_COPY, ALL_COPY }; namespace core { +extern std::atomic_bool SAVE_PUS_SEQUENCE_COUNT; +extern std::atomic_bool SAVE_CFDP_SEQUENCE_COUNT; + // TODO: Support for status? Or maybe some command to quickly get information whether a unit // is running. enum SystemctlCmd : uint8_t { START = 0, STOP = 1, RESTART = 2, NUM_CMDS = 3 }; @@ -41,9 +44,13 @@ enum SystemctlCmd : uint8_t { START = 0, STOP = 1, RESTART = 2, NUM_CMDS = 3 }; static constexpr char CONF_FOLDER[] = "conf"; static constexpr char VERSION_FILE_NAME[] = "version.txt"; -static constexpr char REBOOT_FILE_NAME[] = "reboot.txt"; +static constexpr char LEGACY_REBOOT_WATCHDOG_FILE_NAME[] = "reboot.txt"; +static constexpr char REBOOT_WATCHDOG_FILE_NAME[] = "reboot_watchdog.txt"; +static constexpr char REBOOT_COUNTER_FILE_NAME[] = "reboot_counters.txt"; static constexpr char TIME_FILE_NAME[] = "time_backup.txt"; +static constexpr uint32_t SYS_ROM_BASE_ADDR = 0x80000000; + static constexpr ActionId_t ANNOUNCE_VERSION = 1; static constexpr ActionId_t ANNOUNCE_CURRENT_IMAGE = 2; static constexpr ActionId_t ANNOUNCE_BOOT_COUNTS = 3; @@ -51,6 +58,7 @@ static constexpr ActionId_t SWITCH_REBOOT_FILE_HANDLING = 5; static constexpr ActionId_t RESET_REBOOT_COUNTERS = 6; static constexpr ActionId_t SWITCH_IMG_LOCK = 7; static constexpr ActionId_t SET_MAX_REBOOT_CNT = 8; +static constexpr ActionId_t READ_REBOOT_MECHANISM_INFO = 9; static constexpr ActionId_t OBSW_UPDATE_FROM_SD_0 = 10; static constexpr ActionId_t OBSW_UPDATE_FROM_SD_1 = 11; @@ -113,6 +121,14 @@ static constexpr Event TRYING_I2C_RECOVERY = event::makeEvent(SUBSYSTEM_ID, 10, static constexpr Event I2C_REBOOT = event::makeEvent(SUBSYSTEM_ID, 11, severity::HIGH); //! [EXPORT] : [COMMENT] PDEC recovery through reset was not possible, performing full reboot. static constexpr Event PDEC_REBOOT = event::makeEvent(SUBSYSTEM_ID, 12, severity::HIGH); +//! [EXPORT] : [COMMENT] Version information of the firmware (not OBSW). +//! 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. +static constexpr Event FIRMWARE_INFO = event::makeEvent(SUBSYSTEM_ID, 13, severity::INFO); +//! [EXPORT] : [COMMENT] Active SD card info. SD States: 0: OFF, 1: ON, 2: MOUNTED. +//! P1: Active SD Card Index, 0 if none is active +//! P2: First two bytes: SD state of SD card 0, last two bytes SD state of SD card 1 +static constexpr Event ACTIVE_SD_INFO = event::makeEvent(SUBSYSTEM_ID, 14, severity::INFO); class ListDirectoryCmdBase { public: // TODO: Packet definition for clean deserialization @@ -242,19 +258,22 @@ class CpHelperParser { CpHelperParser(const uint8_t* data, size_t maxLen) : data(data), maxLen(maxLen) {} ReturnValue_t parse() { - if (maxLen < 1) { + if (maxLen < 2) { return SerializeIF::STREAM_TOO_SHORT; } recursiveOpt = data[0]; - return parseDestTargetString(data + 1, maxLen - 1, destTgt); + forceOpt = data[1]; + return parseDestTargetString(data + 2, maxLen - 2, destTgt); } const SourceTargetPair& destTgtPair() const { return destTgt; } bool isRecursiveOptSet() const { return recursiveOpt; } + bool isForceOptSet() const { return forceOpt; } private: const uint8_t* data; size_t maxLen; bool recursiveOpt = false; + bool forceOpt = false; SourceTargetPair destTgt; }; diff --git a/mission/system/EiveSystem.cpp b/mission/system/EiveSystem.cpp index f17b82cb..41f52cde 100644 --- a/mission/system/EiveSystem.cpp +++ b/mission/system/EiveSystem.cpp @@ -195,10 +195,21 @@ void EiveSystem::i2cRecoveryLogic() { // Try recovery. executeAction(EXECUTE_I2C_REBOOT, MessageQueueIF::NO_QUEUE, nullptr, 0); } else { + if (waitingForI2cReboot) { + return; + } triggerEvent(core::I2C_REBOOT); + // Some delay to ensure that the event is stored in the persistent TM store as well. + TaskFactory::delayTask(500); // We already tried an I2C recovery but the bus is still broken. - // Send full reboot request to core controller. - sendFullRebootCommand(); + // Send reboot request to core controller. + result = sendSelfRebootCommand(); + if (result != returnvalue::OK) { + sif::error << "Sending a reboot command has failed" << std::endl; + // If the previous operation failed, it should be re-attempted the next task cycle. + return; + } + waitingForI2cReboot = true; return; } } @@ -285,25 +296,38 @@ ReturnValue_t EiveSystem::sendFullRebootCommand() { } void EiveSystem::pdecRecoveryLogic() { - if (ptmeResetWasAttempted and ptmeResetWasAttemptedCd.hasTimedOut()) { - ptmeResetWasAttempted = false; + if (pdecResetWasAttempted and pdecResetWasAttemptedCd.hasTimedOut()) { + pdecResetWasAttempted = false; } if (frameDirtyCheckCd.hasTimedOut()) { if (frameDirtyErrorCounter >= FRAME_DIRTY_COM_REBOOT_LIMIT) { // If a PTME reset was already attempted and there is still an issue receiving TC frames, // reboot the system. - if (ptmeResetWasAttempted) { + if (pdecResetWasAttempted) { + if (waitingForPdecReboot) { + return; + } triggerEvent(core::PDEC_REBOOT); + // Some delay to ensure that the event is stored in the persistent TM store as well. + TaskFactory::delayTask(500); // Send reboot command. - sendFullRebootCommand(); + ReturnValue_t result = sendSelfRebootCommand(); + if (result != returnvalue::OK) { + sif::error << "Sending a reboot command has failed" << std::endl; + // If the previous operation failed, it should be re-attempted the next task cycle. + pdecResetWasAttemptedCd.resetTimer(); + return; + } + waitingForPdecReboot = true; + return; } else { // Try one full PDEC reset. CommandMessage msg; store_address_t dummy{}; ActionMessage::setCommand(&msg, pdec::RESET_PDEC_WITH_REINIITALIZATION, dummy); commandQueue->sendMessage(pdecHandlerQueueId, &msg); - ptmeResetWasAttemptedCd.resetTimer(); - ptmeResetWasAttempted = true; + pdecResetWasAttemptedCd.resetTimer(); + pdecResetWasAttempted = true; } } frameDirtyErrorCounter = 0; @@ -329,3 +353,17 @@ ReturnValue_t EiveSystem::handleCommandMessage(CommandMessage* message) { } return Subsystem::handleCommandMessage(message); } + +ReturnValue_t EiveSystem::sendSelfRebootCommand() { + CommandMessage msg; + uint8_t data[1]; + // This option is used to target the same image. + data[0] = true; + store_address_t storeId; + ReturnValue_t result = IPCStore->addData(&storeId, data, sizeof(data)); + if (result != returnvalue::OK) { + return result; + } + ActionMessage::setCommand(&msg, core::XSC_REBOOT_OBC, storeId); + return commandQueue->sendMessage(coreCtrlQueueId, &msg); +} diff --git a/mission/system/EiveSystem.h b/mission/system/EiveSystem.h index 67f11c3e..c724ba34 100644 --- a/mission/system/EiveSystem.h +++ b/mission/system/EiveSystem.h @@ -39,8 +39,10 @@ class EiveSystem : public Subsystem, public HasActionsIF { Countdown frameDirtyCheckCd = Countdown(10000); // If the PDEC reset was already attempted in the last 2 minutes, there is a high chance that // only a full reboot will fix the issue. - Countdown ptmeResetWasAttemptedCd = Countdown(120000); - bool ptmeResetWasAttempted = false; + Countdown pdecResetWasAttemptedCd = Countdown(120000); + bool pdecResetWasAttempted = false; + bool waitingForI2cReboot = false; + bool waitingForPdecReboot = false; ActionHelper actionHelper; PowerSwitchIF* powerSwitcher = nullptr; @@ -63,6 +65,7 @@ class EiveSystem : public Subsystem, public HasActionsIF { ReturnValue_t handleCommandMessage(CommandMessage* message) override; ReturnValue_t sendFullRebootCommand(); + ReturnValue_t sendSelfRebootCommand(); void pdecRecoveryLogic(); diff --git a/mission/system/systemTree.cpp b/mission/system/systemTree.cpp index 41bf3216..10c40617 100644 --- a/mission/system/systemTree.cpp +++ b/mission/system/systemTree.cpp @@ -31,12 +31,12 @@ void buildPtgInertialSequence(Subsystem& ss, ModeListEntry& eh); static const auto OFF = HasModesIF::MODE_OFF; static const auto NML = DeviceHandlerIF::MODE_NORMAL; -void satsystem::init() { +void satsystem::init(bool commandPlPcdu1) { auto& acsSubsystem = acs::init(); acsSubsystem.connectModeTreeParent(EIVE_SYSTEM); auto& payloadSubsystem = payload::init(); payloadSubsystem.connectModeTreeParent(EIVE_SYSTEM); - auto& tcsSubsystem = tcs::init(); + auto& tcsSubsystem = tcs::init(commandPlPcdu1); tcsSubsystem.connectModeTreeParent(EIVE_SYSTEM); auto& comSubsystem = com::init(); comSubsystem.connectModeTreeParent(EIVE_SYSTEM); diff --git a/mission/system/systemTree.h b/mission/system/systemTree.h index 9d769277..0ed73a89 100644 --- a/mission/system/systemTree.h +++ b/mission/system/systemTree.h @@ -5,7 +5,7 @@ namespace satsystem { -void init(); +void init(bool commandPlPcdu1); extern EiveSystem EIVE_SYSTEM; diff --git a/mission/system/tcs/TmpDevFdir.cpp b/mission/system/tcs/TmpDevFdir.cpp index d501dd1a..8c2b1706 100644 --- a/mission/system/tcs/TmpDevFdir.cpp +++ b/mission/system/tcs/TmpDevFdir.cpp @@ -20,7 +20,7 @@ ReturnValue_t TmpDevFdir::eventReceived(EventMessage* event) { // We'll try a recovery as long as defined in MAX_REBOOT. // Might cause some AssemblyBase cycles, so keep number low. // Ignored for TMP device, no way to power cycle it without going to OFF/BOOT mode. - // handleRecovery(event->getEvent()); + setFaulty(event->getEvent()); break; case DeviceHandlerIF::DEVICE_INTERPRETING_REPLY_FAILED: case DeviceHandlerIF::DEVICE_READING_REPLY_FAILED: @@ -28,8 +28,10 @@ ReturnValue_t TmpDevFdir::eventReceived(EventMessage* event) { case DeviceHandlerIF::DEVICE_UNKNOWN_REPLY: // Some DH's generate generic reply-ids. case DeviceHandlerIF::DEVICE_BUILDING_COMMAND_FAILED: // These faults all mean that there were stupid replies from a device. - // With now way to do a recovery, set the device to faulty immediately. - setFaulty(event->getEvent()); + // With no way to do a recovery, set the device to faulty instead of trying a recovery. + if (strangeReplyCount.incrementAndCheck()) { + setFaulty(event->getEvent()); + } break; case DeviceHandlerIF::DEVICE_SENDING_COMMAND_FAILED: case DeviceHandlerIF::DEVICE_REQUESTING_REPLY_FAILED: @@ -40,7 +42,9 @@ ReturnValue_t TmpDevFdir::eventReceived(EventMessage* event) { break; } // else - setFaulty(event->getEvent()); + if (missedReplyCount.incrementAndCheck()) { + setFaulty(event->getEvent()); + } break; case StorageManagerIF::GET_DATA_FAILED: case StorageManagerIF::STORE_DATA_FAILED: diff --git a/mission/system/tcs/tcsModeTree.cpp b/mission/system/tcs/tcsModeTree.cpp index 6cc57c6f..09a4fa61 100644 --- a/mission/system/tcs/tcsModeTree.cpp +++ b/mission/system/tcs/tcsModeTree.cpp @@ -10,8 +10,8 @@ TcsSubsystem satsystem::tcs::SUBSYSTEM(objects::TCS_SUBSYSTEM, 12, 24); namespace { // Alias for checker function const auto check = subsystem::checkInsert; -void buildOffSequence(Subsystem& ss, ModeListEntry& eh); -void buildNormalSequence(Subsystem& ss, ModeListEntry& eh); +void buildOffSequence(Subsystem& ss, ModeListEntry& eh, bool commandPlPcdu1); +void buildNormalSequence(Subsystem& ss, ModeListEntry& eh, bool commandPlPcdu1); } // namespace static const auto OFF = HasModesIF::MODE_OFF; @@ -27,17 +27,17 @@ auto TCS_TABLE_NORMAL_TGT = std::make_pair((NML << 24) | 1, FixedArrayList()); auto TCS_TABLE_NORMAL_TRANS_1 = std::make_pair((NML << 24) | 3, FixedArrayList()); -Subsystem& satsystem::tcs::init() { +Subsystem& satsystem::tcs::init(bool commandPlPcdu1) { ModeListEntry entry; - buildOffSequence(SUBSYSTEM, entry); - buildNormalSequence(SUBSYSTEM, entry); + buildOffSequence(SUBSYSTEM, entry, commandPlPcdu1); + buildNormalSequence(SUBSYSTEM, entry, commandPlPcdu1); SUBSYSTEM.setInitialMode(OFF); return SUBSYSTEM; } namespace { -void buildOffSequence(Subsystem& ss, ModeListEntry& eh) { +void buildOffSequence(Subsystem& ss, ModeListEntry& eh, bool commandPlPcdu1) { std::string context = "satsystem::tcs::buildOffSequence"; auto ctxc = context.c_str(); // Insert Helper Table @@ -67,7 +67,9 @@ void buildOffSequence(Subsystem& ss, ModeListEntry& eh) { iht(objects::TMP1075_HANDLER_TCS_0, OFF, 0, TCS_TABLE_OFF_TRANS_1.second); iht(objects::TMP1075_HANDLER_TCS_1, OFF, 0, TCS_TABLE_OFF_TRANS_1.second); iht(objects::TMP1075_HANDLER_PLPCDU_0, OFF, 0, TCS_TABLE_OFF_TRANS_1.second); - // TMP PL PCDU 1 is damaged + if (commandPlPcdu1) { + iht(objects::TMP1075_HANDLER_PLPCDU_1, OFF, 0, TCS_TABLE_OFF_TRANS_1.second); + } iht(objects::TMP1075_HANDLER_IF_BOARD, OFF, 0, TCS_TABLE_OFF_TRANS_1.second); check(ss.addTable(TableEntry(TCS_TABLE_OFF_TRANS_1.first, &TCS_TABLE_OFF_TRANS_1.second)), ctxc); @@ -79,7 +81,7 @@ void buildOffSequence(Subsystem& ss, ModeListEntry& eh) { ctxc); } -void buildNormalSequence(Subsystem& ss, ModeListEntry& eh) { +void buildNormalSequence(Subsystem& ss, ModeListEntry& eh, bool commandPlPcdu1) { std::string context = "satsystem::tcs::buildNormalSequence"; auto ctxc = context.c_str(); // Insert Helper Table @@ -105,7 +107,9 @@ void buildNormalSequence(Subsystem& ss, ModeListEntry& eh) { iht(objects::TMP1075_HANDLER_TCS_0, NML, 0, TCS_TABLE_NORMAL_TRANS_0.second); iht(objects::TMP1075_HANDLER_TCS_1, NML, 0, TCS_TABLE_NORMAL_TRANS_0.second); iht(objects::TMP1075_HANDLER_PLPCDU_0, NML, 0, TCS_TABLE_NORMAL_TRANS_0.second); - // TMP PL PCDU 1 is damaged + if (commandPlPcdu1) { + iht(objects::TMP1075_HANDLER_PLPCDU_1, NML, 0, TCS_TABLE_NORMAL_TRANS_0.second); + } iht(objects::TMP1075_HANDLER_IF_BOARD, NML, 0, TCS_TABLE_NORMAL_TRANS_0.second); check(ss.addTable(TableEntry(TCS_TABLE_NORMAL_TRANS_0.first, &TCS_TABLE_NORMAL_TRANS_0.second)), ctxc); diff --git a/mission/system/tcs/tcsModeTree.h b/mission/system/tcs/tcsModeTree.h index e5973641..4370ae73 100644 --- a/mission/system/tcs/tcsModeTree.h +++ b/mission/system/tcs/tcsModeTree.h @@ -7,7 +7,7 @@ namespace satsystem { namespace tcs { extern TcsSubsystem SUBSYSTEM; -Subsystem& init(); +Subsystem& init(bool commandPlPcdu1); } // namespace tcs } // namespace satsystem diff --git a/mission/tcs/HeaterHandler.cpp b/mission/tcs/HeaterHandler.cpp index de4b600d..b88dfe75 100644 --- a/mission/tcs/HeaterHandler.cpp +++ b/mission/tcs/HeaterHandler.cpp @@ -51,9 +51,13 @@ ReturnValue_t HeaterHandler::performOperation(uint8_t operationCode) { if (mainLineSwitcher->getSwitchState(mainLineSwitch) == SWITCH_OFF) { waitForSwitchOff = false; mode = MODE_OFF; + busyWithSwitchCommanding = false; modeHelper.modeChanged(mode, submode); } } + if (busyWithSwitchCommanding and heaterCmdBusyCd.hasTimedOut()) { + busyWithSwitchCommanding = false; + } } catch (const std::out_of_range& e) { sif::warning << "HeaterHandler::performOperation: " "Out of range error | " @@ -93,7 +97,7 @@ ReturnValue_t HeaterHandler::initialize() { ReturnValue_t HeaterHandler::initializeHeaterMap() { for (power::Switch_t switchNr = 0; switchNr < heater::NUMBER_OF_SWITCHES; switchNr++) { - heaterVec.push_back(HeaterWrapper(helper.heaters[switchNr], SwitchState::OFF)); + heaterVec.push_back(HeaterWrapper(helper.heaters[switchNr], heater::SwitchState::OFF)); } return returnvalue::OK; } @@ -101,23 +105,23 @@ ReturnValue_t HeaterHandler::initializeHeaterMap() { void HeaterHandler::readCommandQueue() { ReturnValue_t result = returnvalue::OK; CommandMessage command; - do { + if (not busyWithSwitchCommanding) { result = commandQueue->receiveMessage(&command); if (result == MessageQueueIF::EMPTY) { - break; + return; } else if (result != returnvalue::OK) { sif::warning << "HeaterHandler::readCommandQueue: Message reception error" << std::endl; - break; - } - result = actionHelper.handleActionMessage(&command); - if (result == returnvalue::OK) { - continue; + return; } result = modeHelper.handleModeCommand(&command); if (result == returnvalue::OK) { - continue; + return; } - } while (result == returnvalue::OK); + result = actionHelper.handleActionMessage(&command); + if (result == returnvalue::OK) { + return; + } + } } ReturnValue_t HeaterHandler::executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, @@ -167,6 +171,8 @@ ReturnValue_t HeaterHandler::executeAction(ActionId_t actionId, MessageQueueId_t heater.action = action; heater.cmdActive = true; heater.replyQueue = commandedBy; + busyWithSwitchCommanding = true; + heaterCmdBusyCd.resetTimer(); return returnvalue::OK; } @@ -208,7 +214,7 @@ ReturnValue_t HeaterHandler::sendSwitchCommand(uint8_t switchNr, ReturnValue_t o void HeaterHandler::handleSwitchHandling() { for (uint8_t idx = 0; idx < heater::NUMBER_OF_SWITCHES; idx++) { auto health = heaterVec[idx].healthDevice->getHealth(); - if (heaterVec[idx].switchState == SwitchState::ON) { + if (heaterVec[idx].switchState == heater::SwitchState::ON) { // If a heater is still on but the device was marked faulty by the operator, the SW // will shut down the heater if (health == HasHealthIF::FAULTY or health == HasHealthIF::PERMANENT_FAULTY) { @@ -249,6 +255,7 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) { sif::error << "HeaterHandler::handleSwitchOnCommand: Main switch setting on timeout" << std::endl; heater.cmdActive = false; + busyWithSwitchCommanding = false; heater.waitMainSwitchOn = false; if (heater.replyQueue != commandQueue->getId()) { actionHelper.finish(false, heater.replyQueue, heater.action, MAIN_SWITCH_SET_TIMEOUT); @@ -259,27 +266,25 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) { // Check state of main line switch ReturnValue_t mainSwitchState = mainLineSwitcher->getSwitchState(mainLineSwitch); if (mainSwitchState == PowerSwitchIF::SWITCH_ON) { - if (getSwitchState(heaterIdx) == SwitchState::OFF) { - gpioId_t gpioId = heater.gpioId; - result = gpioInterface->pullHigh(gpioId); - if (result != returnvalue::OK) { - sif::error << "HeaterHandler::handleSwitchOnCommand: Failed to pull gpio with id " << gpioId - << " high" << std::endl; - triggerEvent(GPIO_PULL_HIGH_FAILED, result); - } else { - triggerEvent(HEATER_WENT_ON, heaterIdx, 0); - EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO, - MODE_ON, 0); - { - MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); - heater.switchState = ON; - } - } - } else { - triggerEvent(SWITCH_ALREADY_ON, heaterIdx); + gpioId_t gpioId = heater.gpioId; + result = gpioInterface->pullHigh(gpioId); + if (result != returnvalue::OK) { + sif::error << "HeaterHandler::handleSwitchOnCommand: Failed to pull GPIO with ID " << gpioId + << " high" << std::endl; + triggerEvent(GPIO_PULL_HIGH_FAILED, result); + } + if (result == returnvalue::OK) { + triggerEvent(HEATER_WENT_ON, heaterIdx, 0); + { + MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); + heater.switchState = heater::SwitchState::ON; + } + EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO, + MODE_ON, 0); + busyWithSwitchCommanding = false; + mode = HasModesIF::MODE_ON; + modeHelper.modeChanged(mode, submode); } - mode = HasModesIF::MODE_ON; - modeHelper.modeChanged(mode, submode); // There is no need to send action finish replies if the sender was the // HeaterHandler itself if (heater.replyQueue != commandQueue->getId()) { @@ -312,30 +317,33 @@ void HeaterHandler::handleSwitchOnCommand(heater::Switch heaterIdx) { void HeaterHandler::handleSwitchOffCommand(heater::Switch heaterIdx) { ReturnValue_t result = returnvalue::OK; auto& heater = heaterVec.at(heaterIdx); - // Check whether switch is already off - if (getSwitchState(heaterIdx)) { - gpioId_t gpioId = heater.gpioId; - result = gpioInterface->pullLow(gpioId); - if (result != returnvalue::OK) { - sif::error << "HeaterHandler::handleSwitchOffCommand: Failed to pull gpio with id" << gpioId - << " low" << std::endl; - triggerEvent(GPIO_PULL_LOW_FAILED, result); - } else { + gpioId_t gpioId = heater.gpioId; + result = gpioInterface->pullLow(gpioId); + if (result != returnvalue::OK) { + sif::error << "HeaterHandler::handleSwitchOffCommand: Failed to pull gpio with id" << gpioId + << " low" << std::endl; + triggerEvent(GPIO_PULL_LOW_FAILED, result); + } + if (result == returnvalue::OK) { + // Check whether switch is already off + if (getSwitchState(heaterIdx) == heater::SwitchState::ON) { { MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); - heater.switchState = OFF; + heater.switchState = heater::SwitchState::OFF; } triggerEvent(HEATER_WENT_OFF, heaterIdx, 0); - EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO, - MODE_OFF, 0); - // When all switches are off, also main line switch will be turned off - if (allSwitchesOff()) { - mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF); - waitForSwitchOff = true; - } + } else { + triggerEvent(SWITCH_ALREADY_OFF, heaterIdx); + } + EventManagerIF::triggerEvent(helper.heaters[heaterIdx].first->getObjectId(), MODE_INFO, + MODE_OFF, 0); + // When all switches are off, also main line switch will be turned off + if (allSwitchesOff()) { + mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF); + waitForSwitchOff = true; + } else { + busyWithSwitchCommanding = false; } - } else { - triggerEvent(SWITCH_ALREADY_OFF, heaterIdx); } if (heater.replyQueue != NO_COMMANDER) { // Report back switch command reply if necessary @@ -348,15 +356,15 @@ void HeaterHandler::handleSwitchOffCommand(heater::Switch heaterIdx) { heater.cmdActive = false; } -HeaterHandler::SwitchState HeaterHandler::getSwitchState(heater::Switch switchNr) const { +heater::SwitchState HeaterHandler::getSwitchState(heater::Switch switchNr) const { MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); return heaterVec.at(switchNr).switchState; } -ReturnValue_t HeaterHandler::switchHeater(heater::Switch heater, SwitchState switchState) { - if (switchState == SwitchState::ON) { +ReturnValue_t HeaterHandler::switchHeater(heater::Switch heater, heater::SwitchState switchState) { + if (switchState == heater::SwitchState::ON) { return sendSwitchCommand(heater, PowerSwitchIF::SWITCH_ON); - } else if (switchState == SwitchState::OFF) { + } else if (switchState == heater::SwitchState::OFF) { return sendSwitchCommand(heater, PowerSwitchIF::SWITCH_OFF); } return returnvalue::FAILED; @@ -365,10 +373,10 @@ ReturnValue_t HeaterHandler::switchHeater(heater::Switch heater, SwitchState swi void HeaterHandler::announceMode(bool recursive) { triggerEvent(MODE_INFO, mode, submode); - std::array states; + std::array states; getAllSwitchStates(states); for (unsigned idx = 0; idx < helper.heaters.size(); idx++) { - if (states[idx] == ON) { + if (states[idx] == heater::SwitchState::ON) { EventManagerIF::triggerEvent(helper.heaters[idx].first->getObjectId(), MODE_INFO, MODE_ON, 0); } else { EventManagerIF::triggerEvent(helper.heaters[idx].first->getObjectId(), MODE_INFO, MODE_OFF, @@ -397,7 +405,7 @@ ModeTreeChildIF& HeaterHandler::getModeTreeChildIF() { return *this; } object_id_t HeaterHandler::getObjectId() const { return SystemObject::getObjectId(); } -ReturnValue_t HeaterHandler::getAllSwitchStates(std::array& statesBuf) { +ReturnValue_t HeaterHandler::getAllSwitchStates(std::array& statesBuf) { { MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); if (mg.getLockResult() != returnvalue::OK) { @@ -411,13 +419,13 @@ ReturnValue_t HeaterHandler::getAllSwitchStates(std::array& stat } bool HeaterHandler::allSwitchesOff() { - bool allSwitchesOrd = false; MutexGuard mg(handlerLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); - /* Or all switches. As soon one switch is on, allSwitchesOrd will be true */ for (power::Switch_t switchNr = 0; switchNr < heater::NUMBER_OF_SWITCHES; switchNr++) { - allSwitchesOrd = allSwitchesOrd || heaterVec.at(switchNr).switchState; + if (heaterVec.at(switchNr).switchState == heater::SwitchState::ON) { + return false; + } } - return !allSwitchesOrd; + return true; } MessageQueueId_t HeaterHandler::getCommandQueue() const { return commandQueue->getId(); } @@ -432,7 +440,7 @@ ReturnValue_t HeaterHandler::getSwitchState(uint8_t switchNr) const { if (switchNr > 7) { return returnvalue::FAILED; } - if (getSwitchState(static_cast(switchNr)) == SwitchState::ON) { + if (getSwitchState(static_cast(switchNr)) == heater::SwitchState::ON) { return PowerSwitchIF::SWITCH_ON; } return PowerSwitchIF::SWITCH_OFF; diff --git a/mission/tcs/HeaterHandler.h b/mission/tcs/HeaterHandler.h index 609ac725..4901c505 100644 --- a/mission/tcs/HeaterHandler.h +++ b/mission/tcs/HeaterHandler.h @@ -60,7 +60,6 @@ class HeaterHandler : public ExecutableObjectIF, static const ReturnValue_t COMMAND_ALREADY_WAITING = MAKE_RETURN_CODE(0xA5); enum CmdSourceParam : uint8_t { INTERNAL = 0, EXTERNAL = 1 }; - enum SwitchState : uint8_t { ON = 1, OFF = 0 }; /** Device command IDs */ static const DeviceCommandId_t SWITCH_HEATER = 0x0; @@ -69,14 +68,14 @@ class HeaterHandler : public ExecutableObjectIF, PowerSwitchIF* mainLineSwitcherObjectId, power::Switch_t mainLineSwitch); ReturnValue_t connectModeTreeParent(HasModeTreeChildrenIF& parent) override; - ReturnValue_t getAllSwitchStates(std::array& statesBuf); + ReturnValue_t getAllSwitchStates(std::array& statesBuf); virtual ~HeaterHandler(); protected: enum SwitchAction : uint8_t { SET_SWITCH_OFF, SET_SWITCH_ON, NONE }; - ReturnValue_t switchHeater(heater::Switch heater, SwitchState switchState); + ReturnValue_t switchHeater(heater::Switch heater, heater::SwitchState switchState); HasHealthIF::HealthState getHealth(heater::Switch heater); ReturnValue_t performOperation(uint8_t operationCode = 0) override; @@ -121,14 +120,14 @@ class HeaterHandler : public ExecutableObjectIF, * @param mainSwitchCountdown Sets timeout to wait for main switch being set on. */ struct HeaterWrapper { - HeaterWrapper(HeaterPair pair, SwitchState initState) + HeaterWrapper(HeaterPair pair, heater::SwitchState initState) : healthDevice(pair.first), gpioId(pair.second), switchState(initState) {} HealthDevice* healthDevice = nullptr; gpioId_t gpioId = gpio::NO_GPIO; SwitchAction action = SwitchAction::NONE; MessageQueueId_t replyQueue = MessageQueueIF::NO_QUEUE; bool cmdActive = false; - SwitchState switchState = SwitchState::OFF; + heater::SwitchState switchState = heater::SwitchState::OFF; bool waitMainSwitchOn = false; Countdown mainSwitchCountdown; }; @@ -148,6 +147,7 @@ class HeaterHandler : public ExecutableObjectIF, /** Size of command queue */ size_t cmdQueueSize = 20; bool waitForSwitchOff = true; + bool busyWithSwitchCommanding = false; GpioIF* gpioInterface = nullptr; @@ -163,6 +163,7 @@ class HeaterHandler : public ExecutableObjectIF, power::Switch_t mainLineSwitch; ActionHelper actionHelper; + Countdown heaterCmdBusyCd = Countdown(2000); StorageManagerIF* ipcStore = nullptr; @@ -175,7 +176,7 @@ class HeaterHandler : public ExecutableObjectIF, * @brief Returns the state of a switch (ON - true, or OFF - false). * @param switchNr The number of the switch to check. */ - SwitchState getSwitchState(heater::Switch switchNr) const; + heater::SwitchState getSwitchState(heater::Switch switchNr) const; /** * @brief This function runs commands waiting for execution. diff --git a/mission/tcs/Max31865EiveHandler.cpp b/mission/tcs/Max31865EiveHandler.cpp index 599071ef..a3c1dce1 100644 --- a/mission/tcs/Max31865EiveHandler.cpp +++ b/mission/tcs/Max31865EiveHandler.cpp @@ -37,6 +37,8 @@ void Max31865EiveHandler::doShutDown() { transitionOk = false; } if (state == InternalState::INACTIVE and transitionOk) { + sensorDataset.temperatureCelcius = thermal::INVALID_TEMPERATURE; + sensorDataset.setValidity(false, true); updatePeriodicReply(false, EiveMax31855::RtdCommands::EXCHANGE_SET_ID); setMode(MODE_OFF); } diff --git a/mission/tcs/Tmp1075Handler.cpp b/mission/tcs/Tmp1075Handler.cpp index df57aa8a..7e8569b6 100644 --- a/mission/tcs/Tmp1075Handler.cpp +++ b/mission/tcs/Tmp1075Handler.cpp @@ -1,4 +1,5 @@ #include +#include #include #include @@ -11,14 +12,12 @@ Tmp1075Handler::Tmp1075Handler(object_id_t objectId, object_id_t comIF, CookieIF Tmp1075Handler::~Tmp1075Handler() {} -void Tmp1075Handler::doStartUp() { - if (getMode() == _MODE_START_UP) { - setMode(MODE_ON); - } -} +void Tmp1075Handler::doStartUp() { setMode(MODE_ON); } void Tmp1075Handler::doShutDown() { communicationStep = CommunicationStep::START_ADC_CONVERSION; + PoolReadGuard pg(&dataset); + dataset.setValidity(false, true); setMode(_MODE_POWER_DOWN); } @@ -86,8 +85,11 @@ ReturnValue_t Tmp1075Handler::scanForReply(const uint8_t *start, size_t remainin ReturnValue_t Tmp1075Handler::interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) { switch (id) { case TMP1075::GET_TEMP: { - int16_t tempValueRaw = 0; - tempValueRaw = packet[0] << 4 | packet[1] >> 4; + // Convert 12 bit MSB first raw temperature to 16 bit first. + int16_t tempValueRaw = static_cast((packet[0] << 8) | packet[1]) >> 4; + // Sign extension to 16 bits: If the sign bit is set, fill up with ones on the left. + tempValueRaw = (packet[0] & 0x80) ? (tempValueRaw | 0xF000) : tempValueRaw; + // 0.0625 is the sensor sensitivity. float tempValue = ((static_cast(tempValueRaw)) * 0.0625); #if OBSW_DEBUG_TMP1075 == 1 sif::info << "Tmp1075 with object id: 0x" << std::hex << getObjectId() @@ -131,12 +133,14 @@ ReturnValue_t Tmp1075Handler::initializeLocalDataPool(localpool::DataPool &local return returnvalue::OK; } -void Tmp1075Handler::setModeNormal() { setMode(_MODE_TO_NORMAL); } - ReturnValue_t Tmp1075Handler::setHealth(HealthState health) { if (health != FAULTY and health != PERMANENT_FAULTY and health != HEALTHY and health != EXTERNAL_CONTROL) { return returnvalue::FAILED; } - return returnvalue::OK; + // Required because we do not have an assembly. + if (health == FAULTY or health == PERMANENT_FAULTY) { + setMode(_MODE_SHUT_DOWN); + } + return DeviceHandlerBase::setHealth(health); } diff --git a/mission/tcs/Tmp1075Handler.h b/mission/tcs/Tmp1075Handler.h index 02fd6823..4badca39 100644 --- a/mission/tcs/Tmp1075Handler.h +++ b/mission/tcs/Tmp1075Handler.h @@ -20,8 +20,6 @@ class Tmp1075Handler : public DeviceHandlerBase { Tmp1075Handler(object_id_t objectId, object_id_t comIF, CookieIF *comCookie); virtual ~Tmp1075Handler(); - void setModeNormal(); - protected: void doStartUp() override; void doShutDown() override; diff --git a/mission/tcs/defs.h b/mission/tcs/defs.h index 747dbc6d..e2843570 100644 --- a/mission/tcs/defs.h +++ b/mission/tcs/defs.h @@ -4,6 +4,7 @@ #include namespace heater { + enum Switch : uint8_t { HEATER_0_PLOC_PROC_BRD, HEATER_1_PCDU_PDU, @@ -13,9 +14,13 @@ enum Switch : uint8_t { HEATER_5_STR, HEATER_6_DRO, HEATER_7_S_BAND, - NUMBER_OF_SWITCHES + NUMBER_OF_SWITCHES = 8, + HEATER_NONE = 0xff }; -} + +enum SwitchState : uint8_t { ON = 1, OFF = 0 }; + +} // namespace heater namespace tcs { diff --git a/mission/tmtc/CfdpTmFunnel.cpp b/mission/tmtc/CfdpTmFunnel.cpp index 46915b82..93c294a4 100644 --- a/mission/tmtc/CfdpTmFunnel.cpp +++ b/mission/tmtc/CfdpTmFunnel.cpp @@ -15,8 +15,16 @@ const char* CfdpTmFunnel::getName() const { return "CFDP TM Funnel"; } ReturnValue_t CfdpTmFunnel::performOperation(uint8_t) { TmTcMessage currentMessage; + ReturnValue_t status; unsigned int count = 0; - ReturnValue_t status = tmQueue->receiveMessage(¤tMessage); + if (saveSequenceCount) { + status = saveSequenceCountToFile(); + if (status != returnvalue::OK) { + sif::error << "CfdpTmFunnel: Storing sequence count to file has failed" << std::endl; + } + saveSequenceCount = false; + } + status = tmQueue->receiveMessage(¤tMessage); while (status == returnvalue::OK) { status = handlePacket(currentMessage); if (status != returnvalue::OK) { diff --git a/mission/tmtc/CfdpTmFunnel.h b/mission/tmtc/CfdpTmFunnel.h index a4d1bc70..7b9efd34 100644 --- a/mission/tmtc/CfdpTmFunnel.h +++ b/mission/tmtc/CfdpTmFunnel.h @@ -3,6 +3,7 @@ #include +#include #include #include "fsfw/objectmanager/SystemObject.h" @@ -23,7 +24,6 @@ class CfdpTmFunnel : public TmFunnelBase { MessageQueueId_t fileStoreDest; StorageManagerIF& ramToFileStore; - uint16_t sourceSequenceCount = 0; uint16_t cfdpInCcsdsApid; }; #endif // FSFW_EXAMPLE_COMMON_CFDPTMFUNNEL_H diff --git a/mission/tmtc/PersistentTmStore.cpp b/mission/tmtc/PersistentTmStore.cpp index 8174f8a0..2f884dac 100644 --- a/mission/tmtc/PersistentTmStore.cpp +++ b/mission/tmtc/PersistentTmStore.cpp @@ -17,6 +17,8 @@ using namespace returnvalue; +static constexpr bool DEBUG_DUMPS = false; + PersistentTmStore::PersistentTmStore(PersistentTmStoreArgs args) : SystemObject(args.objectId), tmStore(args.tmStore), @@ -32,6 +34,91 @@ ReturnValue_t PersistentTmStore::cancelDump() { return returnvalue::OK; } +ReturnValue_t PersistentTmStore::buildDumpSet(uint32_t fromUnixSeconds, uint32_t upToUnixSeconds) { + using namespace std::filesystem; + std::error_code e; + dumpParams.orderedDumpFilestamps.clear(); + for (auto const& fileOrDir : directory_iterator(basePath)) { + if (not fileOrDir.is_regular_file(e)) { + continue; + } + dumpParams.fileSize = std::filesystem::file_size(fileOrDir.path(), e); + if (e) { + sif::error << "PersistentTmStore: Could not retrieve file size: " << e.message() << std::endl; + continue; + } + + // File empty or can't even read CCSDS header. + if (dumpParams.fileSize <= 6) { + continue; + } + if (dumpParams.fileSize > fileBuf.size()) { + sif::error << "PersistentTmStore: File too large, is deleted" << std::endl; + triggerEvent(persTmStore::FILE_TOO_LARGE, dumpParams.fileSize, fileBuf.size()); + std::filesystem::remove(fileOrDir.path(), e); + continue; + } + const path& file = fileOrDir.path(); + struct tm fileTime {}; + if (pathToTime(file, fileTime) != returnvalue::OK) { + sif::error << "Time extraction for file " << file << "failed" << std::endl; + continue; + } + auto fileEpoch = static_cast(timegm(&fileTime)); + if ((fileEpoch > dumpParams.fromUnixTime) and + (fileEpoch + rolloverDiffSeconds <= dumpParams.untilUnixTime)) { + std::ifstream ifile(file, std::ios::binary); + if (ifile.bad()) { + sif::error << "PersistentTmStore: File is bad" << std::endl; + // TODO: Consider deleting file here? + continue; + } + + if (DEBUG_DUMPS) { + sif::debug << "Inserting file " << fileOrDir.path() << std::endl; + } + DumpIndex dumpIndex; + dumpIndex.epoch = fileEpoch; + // Multiple files for the same time are supported via a special suffix. We simply count the + // number of copies and later try to dump the same number of files with the additional + // suffixes + auto iter = dumpParams.orderedDumpFilestamps.find(dumpIndex); + if (iter != dumpParams.orderedDumpFilestamps.end()) { + dumpIndex.epoch = iter->epoch; + dumpIndex.additionalFiles = iter->additionalFiles + 1; + dumpParams.orderedDumpFilestamps.erase(dumpIndex); + } else { + dumpIndex.additionalFiles = 0; + } + dumpParams.orderedDumpFilestamps.emplace(dumpIndex); + } + } + return returnvalue::OK; +} + +std::optional PersistentTmStore::extractSuffix(const std::string& pathStr) { + std::string numberStr; + // Find the position of the dot at the end of the file path + size_t dotPos = pathStr.find_last_of('.'); + if ((dotPos < pathStr.length()) and not std::isdigit(pathStr[dotPos + 1])) { + return std::nullopt; + } + // Extract the substring after the dot + numberStr = pathStr.substr(dotPos + 1); + std::optional number; + try { + number = std::stoi(numberStr); + if (number.value() > std::numeric_limits::max()) { + return std::nullopt; + } + + } catch (std::invalid_argument& exception) { + sif::error << "PersistentTmStore::extractSuffix: Exception " << exception.what() + << ", invald input string: " << numberStr << std::endl; + } + return number; +} + ReturnValue_t PersistentTmStore::assignAndOrCreateMostRecentFile() { if (not activeFile.has_value()) { return createMostRecentFile(std::nullopt); @@ -41,6 +128,7 @@ ReturnValue_t PersistentTmStore::assignAndOrCreateMostRecentFile() { ReturnValue_t PersistentTmStore::handleCommandQueue(StorageManagerIF& ipcStore, Command_t& execCmd) { + execCmd = CommandMessageIF::CMD_NONE; CommandMessage cmdMessage; ReturnValue_t result = tcQueue->receiveMessage(&cmdMessage); if (result != returnvalue::OK) { @@ -75,9 +163,9 @@ ReturnValue_t PersistentTmStore::handleCommandQueue(StorageManagerIF& ipcStore, result = startDumpFromUpTo(dumpFromUnixSeconds, dumpUntilUnixSeconds); if (result == BUSY_DUMPING) { triggerEvent(persTmStore::BUSY_DUMPING_EVENT); - } else { - execCmd = cmd; + return result; } + execCmd = cmd; } } return result; @@ -159,6 +247,12 @@ bool PersistentTmStore::updateBaseDir() { if (not exists(basePath, e)) { create_directories(basePath); } + // Each file will have the base name as a prefix again + path preparedFullFilePath = basePath / baseName; + basePathSize = preparedFullFilePath.string().length(); + std::memcpy(filePathBuf.data(), preparedFullFilePath.c_str(), basePathSize); + filePathBuf[basePathSize] = '_'; + basePathSize += 1; baseDirUninitialized = false; return true; } @@ -189,12 +283,20 @@ ReturnValue_t PersistentTmStore::startDumpFromUpTo(uint32_t fromUnixSeconds, if (state == State::DUMPING) { return returnvalue::FAILED; } - dumpParams.dirIter = directory_iterator(basePath); - if (dumpParams.dirIter == directory_iterator()) { + auto dirIter = directory_iterator(basePath); + // Directory empty case. + if (dirIter == directory_iterator()) { return returnvalue::FAILED; } dumpParams.fromUnixTime = fromUnixSeconds; dumpParams.untilUnixTime = upToUnixSeconds; + buildDumpSet(fromUnixSeconds, upToUnixSeconds); + // No files in time window found. + if (dumpParams.orderedDumpFilestamps.empty()) { + return DUMP_DONE; + } + dumpParams.dumpIter = dumpParams.orderedDumpFilestamps.begin(); + dumpParams.currentSameFileIdx = std::nullopt; state = State::DUMPING; return loadNextDumpFile(); } @@ -203,49 +305,54 @@ ReturnValue_t PersistentTmStore::loadNextDumpFile() { using namespace std::filesystem; dumpParams.currentSize = 0; std::error_code e; - for (; dumpParams.dirIter != directory_iterator(); dumpParams.dirIter++) { - dumpParams.dirEntry = *dumpParams.dirIter; - if (dumpParams.dirEntry.is_directory(e)) { - continue; - } - dumpParams.fileSize = std::filesystem::file_size(dumpParams.dirEntry.path(), e); - if (e) { - sif::error << "PersistentTmStore: Could not retrieve file size: " << e.message() << std::endl; - continue; - } - // sif::debug << "Path: " << dumpParams.dirEntry.path() << std::endl; - - // File empty or can't even read CCSDS header. - if (dumpParams.fileSize <= 6) { - continue; - } - if (dumpParams.fileSize > fileBuf.size()) { - sif::error << "PersistentTmStore: File too large, is deleted" << std::endl; - triggerEvent(persTmStore::FILE_TOO_LARGE, dumpParams.fileSize, fileBuf.size()); - std::filesystem::remove(dumpParams.dirEntry.path(), e); - continue; - } - const path& file = dumpParams.dirEntry.path(); - struct tm fileTime {}; - if (pathToTime(file, fileTime) != returnvalue::OK) { - sif::error << "Time extraction for file " << file << "failed" << std::endl; - continue; - } - auto fileEpoch = static_cast(timegm(&fileTime)); - if ((fileEpoch > dumpParams.fromUnixTime) and - (fileEpoch + rolloverDiffSeconds <= dumpParams.untilUnixTime)) { - dumpParams.currentFileUnixStamp = fileEpoch; - std::ifstream ifile(file, std::ios::binary); - if (ifile.bad()) { - sif::error << "PersistentTmStore: File is bad" << std::endl; - continue; + // Handle iteration, which does not necessarily have to increment the set iterator + // if there are multiple files for a given timestamp. + auto handleIteration = [&](DumpIndex& dumpIndex) { + if (dumpIndex.additionalFiles > 0) { + if (not dumpParams.currentSameFileIdx.has_value()) { + // Initialize the file index and stay on same file + dumpParams.currentSameFileIdx = 0; + return; + } else if (dumpParams.currentSameFileIdx.value() < dumpIndex.additionalFiles - 1) { + dumpParams.currentSameFileIdx = dumpParams.currentSameFileIdx.value() + 1; + return; } - ifile.read(reinterpret_cast(fileBuf.data()), - static_cast(dumpParams.fileSize)); - // Increment iterator for next cycle. - dumpParams.dirIter++; - return returnvalue::OK; } + // File will change, reset this field for correct state-keeping. + dumpParams.currentSameFileIdx = std::nullopt; + // Increment iterator for next cycle. + dumpParams.dumpIter++; + }; + while (dumpParams.dumpIter != dumpParams.orderedDumpFilestamps.end()) { + DumpIndex dumpIndex = *dumpParams.dumpIter; + timeval tv{}; + tv.tv_sec = dumpIndex.epoch; + size_t fullPathLength = 0; + createFileName(tv, dumpParams.currentSameFileIdx, fullPathLength); + dumpParams.currentFile = + path(std::string(reinterpret_cast(filePathBuf.data()), fullPathLength)); + if (DEBUG_DUMPS) { + sif::debug << baseName << " dump: Loading " << dumpParams.currentFile << std::endl; + } + dumpParams.fileSize = std::filesystem::file_size(dumpParams.currentFile, e); + if (e) { + // TODO: Event? + sif::error << "PersistentTmStore: Could not load next dump file: " << e.message() + << std::endl; + handleIteration(dumpIndex); + continue; + } + std::ifstream ifile(dumpParams.currentFile, std::ios::binary); + if (ifile.bad()) { + sif::error << "PersistentTmStore: File is bad. Loading next file" << std::endl; + handleIteration(dumpIndex); + continue; + } + ifile.read(reinterpret_cast(fileBuf.data()), + static_cast(dumpParams.fileSize)); + // Next file is loaded, exit the loop. + handleIteration(dumpIndex); + return returnvalue::OK; } // Directory iterator was consumed and we are done. state = State::IDLE; @@ -253,7 +360,10 @@ ReturnValue_t PersistentTmStore::loadNextDumpFile() { } ReturnValue_t PersistentTmStore::getNextDumpPacket(PusTmReader& reader, bool& fileHasSwapped) { - if (state == State::IDLE or dumpParams.pendingPacketDump) { + if (state == State::IDLE) { + return DUMP_DONE; + } + if (dumpParams.pendingPacketDump) { return returnvalue::FAILED; } fileHasSwapped = false; @@ -267,7 +377,11 @@ ReturnValue_t PersistentTmStore::getNextDumpPacket(PusTmReader& reader, bool& fi // Delete the file and load next. Could use better algorithm to partially // restore the file dump, but for now do not trust the file. std::error_code e; - std::filesystem::remove(dumpParams.dirEntry.path().c_str(), e); + std::filesystem::remove(dumpParams.currentFile.c_str(), e); + if (dumpParams.currentFile == activeFile) { + activeFile == std::nullopt; + assignAndOrCreateMostRecentFile(); + } fileHasSwapped = true; return loadNextDumpFile(); } @@ -298,37 +412,9 @@ ReturnValue_t PersistentTmStore::pathToTime(const std::filesystem::path& path, s 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(), config::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)); + size_t currentIdx; + createFileName(currentTv, suffix, currentIdx); + path newPath(std::string(reinterpret_cast(filePathBuf.data()), currentIdx)); std::ofstream of(newPath, std::ios::binary); activeFile = newPath; activeFileTv = currentTv; @@ -350,3 +436,33 @@ void PersistentTmStore::getStartAndEndTimeCurrentOrLastDump(uint32_t& startTime, startTime = dumpParams.fromUnixTime; endTime = dumpParams.untilUnixTime; } + +ReturnValue_t PersistentTmStore::createFileName(timeval& tv, std::optional suffix, + size_t& fullPathLength) { + unsigned currentIdx = basePathSize; + time_t epoch = tv.tv_sec; + struct tm* time = gmtime(&epoch); + size_t writtenBytes = strftime(reinterpret_cast(filePathBuf.data() + currentIdx), + filePathBuf.size(), config::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(filePathBuf.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(filePathBuf.data() + currentIdx), fullSuffix.c_str()); + if (res == nullptr) { + return returnvalue::FAILED; + } + currentIdx += fullSuffix.size(); + } + fullPathLength = currentIdx; + return returnvalue::OK; +} diff --git a/mission/tmtc/PersistentTmStore.h b/mission/tmtc/PersistentTmStore.h index 17d2c9e7..e86acaaf 100644 --- a/mission/tmtc/PersistentTmStore.h +++ b/mission/tmtc/PersistentTmStore.h @@ -10,6 +10,7 @@ #include #include +#include #include "eive/eventSubsystemIds.h" #include "eive/resultClassIds.h" @@ -37,6 +38,14 @@ struct PersistentTmStoreArgs { SdCardMountedIF& sdcMan; }; +struct DumpIndex { + uint32_t epoch = 0; + // Number of additional files with a suffix like .0, .1 etc. + uint8_t additionalFiles = 0; + // Define a custom comparison function based on the epoch variable + bool operator<(const DumpIndex& other) const { return epoch < other.epoch; } +}; + class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject { public: enum class State { IDLE, DUMPING }; @@ -96,7 +105,10 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject { std::string baseName; uint8_t currentSameSecNumber = 0; std::filesystem::path basePath; + // std::filesystem::path pathStart = basePath / baseName; uint32_t rolloverDiffSeconds = 0; + std::array filePathBuf{}; + size_t basePathSize; std::array fileBuf{}; timeval currentTv; timeval activeFileTv{}; @@ -106,8 +118,10 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject { uint32_t fromUnixTime = 0; uint32_t untilUnixTime = 0; uint32_t currentFileUnixStamp = 0; - std::filesystem::directory_iterator dirIter; - std::filesystem::directory_entry dirEntry; + std::filesystem::path currentFile; + std::set orderedDumpFilestamps{}; + std::set::iterator dumpIter; + std::optional currentSameFileIdx = 0; size_t fileSize = 0; size_t currentSize = 0; }; @@ -122,10 +136,13 @@ class PersistentTmStore : public TmStoreFrontendSimpleIF, public SystemObject { [[nodiscard]] MessageQueueId_t getCommandQueue() const override; void calcDiffSeconds(RolloverInterval intervalUnit, uint32_t intervalCount); + ReturnValue_t buildDumpSet(uint32_t fromUnixSeconds, uint32_t upToUnixSeconds); ReturnValue_t createMostRecentFile(std::optional suffix); static ReturnValue_t pathToTime(const std::filesystem::path& path, struct tm& time); void fileToPackets(const std::filesystem::path& path, uint32_t unixStamp); ReturnValue_t loadNextDumpFile(); + ReturnValue_t createFileName(timeval& tv, std::optional suffix, size_t& fullPathLength); + std::optional extractSuffix(const std::string& pathStr); bool updateBaseDir(); ReturnValue_t assignAndOrCreateMostRecentFile(); }; diff --git a/mission/tmtc/PusLiveDemux.cpp b/mission/tmtc/PusLiveDemux.cpp index bbe1be4f..8ba69e07 100644 --- a/mission/tmtc/PusLiveDemux.cpp +++ b/mission/tmtc/PusLiveDemux.cpp @@ -11,23 +11,20 @@ ReturnValue_t PusLiveDemux::demultiplexPackets(StorageManagerIF& tmStore, 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, tmData, tmSize); - if (result == returnvalue::OK) { - message.setStorageId(storeId); - } else { -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PusLiveDemux::handlePacket: Store too full to create data copy" - << std::endl; -#endif - } + if ((destinations.size() > 1) and (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, tmData, tmSize); + if (result == returnvalue::OK) { + message.setStorageId(storeId); } else { - message.setStorageId(origStoreId); +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "PusLiveDemux::handlePacket: Store too full to create data copy" << std::endl; +#endif } + } else { + message.setStorageId(origStoreId); } result = ownerQueue.sendMessage(dest.queueId, &message); if (result != returnvalue::OK) { diff --git a/mission/tmtc/PusTmFunnel.cpp b/mission/tmtc/PusTmFunnel.cpp index dbc8f1c4..87648e55 100644 --- a/mission/tmtc/PusTmFunnel.cpp +++ b/mission/tmtc/PusTmFunnel.cpp @@ -1,5 +1,7 @@ #include "PusTmFunnel.h" +#include + #include "eive/definitions.h" #include "eive/objects.h" #include "fsfw/ipc/CommandMessage.h" @@ -11,8 +13,8 @@ #include "tmtc/pusIds.h" PusTmFunnel::PusTmFunnel(TmFunnelBase::FunnelCfg cfg, StorageManagerIF &ramToFileStore, - TimeReaderIF &timeReader, SdCardMountedIF &sdcMan) - : TmFunnelBase(cfg), ramToFileStore(ramToFileStore), timeReader(timeReader), sdcMan(sdcMan) {} + TimeReaderIF &timeReader) + : TmFunnelBase(cfg), ramToFileStore(ramToFileStore), timeReader(timeReader) {} PusTmFunnel::~PusTmFunnel() = default; @@ -21,6 +23,13 @@ ReturnValue_t PusTmFunnel::performOperation(uint8_t) { ReturnValue_t result; TmTcMessage currentMessage; unsigned int count = 0; + if (saveSequenceCount) { + result = saveSequenceCountToFile(); + if (result != returnvalue::OK) { + sif::error << "PusTmFunnel: Storing sequence count to file has failed" << std::endl; + } + saveSequenceCount = false; + } result = tmQueue->receiveMessage(¤tMessage); while (result == returnvalue::OK) { result = handleTmPacket(currentMessage); @@ -59,7 +68,33 @@ ReturnValue_t PusTmFunnel::handleTmPacket(TmTcMessage &message) { return result; } packet.setSequenceCount(sourceSequenceCount++); + // NOTE: This only works because the limit value bit width is below 16 bits! sourceSequenceCount = sourceSequenceCount % ccsds::LIMIT_SEQUENCE_COUNT; + + // Message type counter handling. + uint8_t service = packet.getService(); + bool insertionFailed = false; + auto mapIter = msgCounterMap.find(service); + if (mapIter == msgCounterMap.end()) { + auto iterPair = msgCounterMap.emplace(service, 0); + if (iterPair.second) { + mapIter = iterPair.first; + } else { + // Should really never never happen but you never know.. + insertionFailed = true; + } + } + if (not insertionFailed) { + packet.setMessageCount(mapIter->second); + // Sane overflow handling. + if (mapIter->second == std::numeric_limits::max()) { + mapIter->second = 0; + } else { + mapIter->second++; + } + } + + // Re-calculate CRC after changing the fields. This operation HAS to come last! packet.updateErrorControl(); // Send to persistent TM store if the packet matches some filter. diff --git a/mission/tmtc/PusTmFunnel.h b/mission/tmtc/PusTmFunnel.h index ba6e8d3e..3696fdcc 100644 --- a/mission/tmtc/PusTmFunnel.h +++ b/mission/tmtc/PusTmFunnel.h @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include "PersistentTmStore.h" @@ -25,7 +27,7 @@ class PusTmFunnel : public TmFunnelBase { public: PusTmFunnel(TmFunnelBase::FunnelCfg cfg, StorageManagerIF &ramToFileStore, - TimeReaderIF &timeReader, SdCardMountedIF &sdcMan); + TimeReaderIF &timeReader); [[nodiscard]] const char *getName() const override; ~PusTmFunnel() override; @@ -36,11 +38,10 @@ class PusTmFunnel : public TmFunnelBase { // Update TV stamp every 5 minutes static constexpr dur_millis_t TV_UPDATE_INTERVAL_SECS = 60 * 5; - uint16_t sourceSequenceCount = 0; + std::map msgCounterMap; StorageManagerIF &ramToFileStore; TimeReaderIF &timeReader; bool storesInitialized = false; - SdCardMountedIF &sdcMan; PusTmRouteByFilterHelper persistentTmMap; ReturnValue_t handleTmPacket(TmTcMessage &message); diff --git a/mission/tmtc/TmFunnelBase.cpp b/mission/tmtc/TmFunnelBase.cpp index eb480b03..fc2e4b76 100644 --- a/mission/tmtc/TmFunnelBase.cpp +++ b/mission/tmtc/TmFunnelBase.cpp @@ -2,6 +2,10 @@ #include +#include +#include +#include + #include "fsfw/ipc/QueueFactory.h" TmFunnelBase::TmFunnelBase(FunnelCfg cfg) @@ -10,7 +14,10 @@ TmFunnelBase::TmFunnelBase(FunnelCfg cfg) tmStore(cfg.tmStore), ipcStore(cfg.ipcStore), tmQueue(QueueFactory::instance()->createMessageQueue(cfg.tmMsgDepth)), - liveDemux(*tmQueue) {} + liveDemux(*tmQueue), + sdcMan(cfg.sdcMan), + sequenceCounterFilename(cfg.sequenceCounterFilename), + saveSequenceCount(cfg.saveSequenceCount) {} ReturnValue_t TmFunnelBase::demultiplexLivePackets(store_address_t origStoreId, const uint8_t *tmData, size_t tmSize) { @@ -27,3 +34,38 @@ void TmFunnelBase::addLiveDestination(const char *name, const AcceptsTelemetryIF &downlinkDestination, uint8_t vcid) { liveDemux.addDestination(name, downlinkDestination, vcid); } + +ReturnValue_t TmFunnelBase::initialize() { + using namespace std::filesystem; + // The filesystem should always be available at the start.. Let's assume it always is, otherwise + // we just live with a regular 0 initialization. It simplifies a lot. + std::error_code e; + path filePath = + path(path(sdcMan.getCurrentMountPrefix()) / path("conf") / path(sequenceCounterFilename)); + if (exists(filePath, e)) { + std::ifstream ifile(filePath); + if (ifile.bad()) { + sif::error << "TmFunnelBase::initialize: Faulty file open for sequence counter initialization" + << std::endl; + return returnvalue::OK; + } + if (not(ifile >> sourceSequenceCount)) { + // Initialize to 0 in any case if reading a number failed. + sourceSequenceCount = 0; + } + } + return returnvalue::OK; +} + +ReturnValue_t TmFunnelBase::saveSequenceCountToFile() { + using namespace std::filesystem; + std::error_code e; + path filePath = + path(path(sdcMan.getCurrentMountPrefix()) / path("conf") / path(sequenceCounterFilename)); + std::ofstream ofile(filePath); + if (ofile.bad()) { + return returnvalue::FAILED; + } + ofile << sourceSequenceCount << "\n"; + return returnvalue::OK; +} diff --git a/mission/tmtc/TmFunnelBase.h b/mission/tmtc/TmFunnelBase.h index 51d16626..72d91103 100644 --- a/mission/tmtc/TmFunnelBase.h +++ b/mission/tmtc/TmFunnelBase.h @@ -6,25 +6,34 @@ #include #include #include +#include #include +#include #include class TmFunnelBase : public AcceptsTelemetryIF, public SystemObject { public: struct FunnelCfg { FunnelCfg(object_id_t objId, const char* name, StorageManagerIF& tmStore, - StorageManagerIF& ipcStore, uint32_t tmMsgDepth) + StorageManagerIF& ipcStore, uint32_t tmMsgDepth, SdCardMountedIF& sdcMan, + const char* sequenceCounterFilename, std::atomic_bool& saveSequenceCount) : objectId(objId), name(name), tmStore(tmStore), ipcStore(ipcStore), - tmMsgDepth(tmMsgDepth) {} + tmMsgDepth(tmMsgDepth), + sdcMan(sdcMan), + sequenceCounterFilename(sequenceCounterFilename), + saveSequenceCount(saveSequenceCount) {} object_id_t objectId; const char* name; StorageManagerIF& tmStore; StorageManagerIF& ipcStore; uint32_t tmMsgDepth; + SdCardMountedIF& sdcMan; + const char* sequenceCounterFilename; + std::atomic_bool& saveSequenceCount; }; explicit TmFunnelBase(FunnelCfg cfg); [[nodiscard]] MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel) const override; @@ -32,6 +41,9 @@ class TmFunnelBase : public AcceptsTelemetryIF, public SystemObject { uint8_t vcid = 0); ReturnValue_t demultiplexLivePackets(store_address_t origStoreId, const uint8_t* tmData, size_t tmSize); + ReturnValue_t initialize() override; + + ReturnValue_t saveSequenceCountToFile(); ~TmFunnelBase() override; @@ -41,6 +53,10 @@ class TmFunnelBase : public AcceptsTelemetryIF, public SystemObject { StorageManagerIF& ipcStore; MessageQueueIF* tmQueue = nullptr; PusLiveDemux liveDemux; + SdCardMountedIF& sdcMan; + const char* sequenceCounterFilename; + std::atomic_bool& saveSequenceCount; + uint16_t sourceSequenceCount = 0; }; #endif /* MISSION_TMTC_TMFUNNELBASE_H_ */ diff --git a/release_checklist.md b/release-checklist.md similarity index 66% rename from release_checklist.md rename to release-checklist.md index 4c8be066..04a0233c 100644 --- a/release_checklist.md +++ b/release-checklist.md @@ -7,7 +7,9 @@ OBSW Release Checklist 2. Re-run the generators with `generators/gen.py all` 3. Re-run the auto-formatters with the `scripts/auto-formatter.sh` script 4. Verify that the Q7S, Q7S EM and Host build are working -5. Wait for CI/CD results +5. Update `CHANGELOG.md`: Add new `unreleased` section, convert old unreleased section to + header containing version number and release date. +6. Wait for CI/CD results # Post-Release diff --git a/tmtc b/tmtc index 2b2ad0a2..957d756d 160000 --- a/tmtc +++ b/tmtc @@ -1 +1 @@ -Subproject commit 2b2ad0a23a8477f81413f10ff4b39053562cabc5 +Subproject commit 957d756d1e0c9862ae18798b4c27f8f0da8a349b diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 20b98979..e9a01891 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -4,7 +4,7 @@ add_subdirectory(mocks) target_sources(${UNITTEST_NAME} PRIVATE main.cpp testEnvironment.cpp - testStampInFilename.cpp + testGenericFilesystem.cpp hdlcEncodingRw.cpp printChar.cpp ) \ No newline at end of file diff --git a/unittest/controller/testThermalController.cpp b/unittest/controller/testThermalController.cpp index c2166ca0..287943ce 100644 --- a/unittest/controller/testThermalController.cpp +++ b/unittest/controller/testThermalController.cpp @@ -30,7 +30,8 @@ TEST_CASE("Thermal Controller", "[ThermalController]") { // testEnvironment::initialize(); - ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler, tcsBrdShortlyUnavailable); + ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler, tcsBrdShortlyUnavailable, + true); ReturnValue_t result = controller.initialize(); REQUIRE(result == returnvalue::OK); diff --git a/unittest/rebootLogic/src/CoreController.cpp b/unittest/rebootLogic/src/CoreController.cpp index 2bc8bbef..23461ea1 100644 --- a/unittest/rebootLogic/src/CoreController.cpp +++ b/unittest/rebootLogic/src/CoreController.cpp @@ -19,116 +19,117 @@ CoreController::CoreController() { setCurrentBootCopy(xsc::CHIP_0, xsc::COPY_0); } -void CoreController::performRebootFileHandling(bool recreateFile) { +void CoreController::performRebootWatchdogHandling(bool recreateFile) { using namespace std; - std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE; + std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE; if (not std::filesystem::exists(path) or recreateFile) { #if OBSW_VERBOSE_LEVEL >= 1 sif::info << "CoreController::performRebootFileHandling: Recreating reboot file" << std::endl; #endif - rebootFile.enabled = true; - rebootFile.img00Cnt = 0; - rebootFile.img01Cnt = 0; - rebootFile.img10Cnt = 0; - rebootFile.img11Cnt = 0; - rebootFile.lastChip = xsc::Chip::CHIP_0; - rebootFile.lastCopy = xsc::Copy::COPY_0; - rebootFile.img00Lock = false; - rebootFile.img01Lock = false; - rebootFile.img10Lock = false; - rebootFile.img11Lock = false; - rebootFile.mechanismNextChip = xsc::Chip::NO_CHIP; - rebootFile.mechanismNextCopy = xsc::Copy::NO_COPY; - rebootFile.bootFlag = false; - rewriteRebootFile(rebootFile); + rebootWatchdogFile.enabled = true; + rebootWatchdogFile.img00Cnt = 0; + rebootWatchdogFile.img01Cnt = 0; + rebootWatchdogFile.img10Cnt = 0; + rebootWatchdogFile.img11Cnt = 0; + rebootWatchdogFile.lastChip = xsc::Chip::CHIP_0; + rebootWatchdogFile.lastCopy = xsc::Copy::COPY_0; + rebootWatchdogFile.img00Lock = false; + rebootWatchdogFile.img01Lock = false; + rebootWatchdogFile.img10Lock = false; + rebootWatchdogFile.img11Lock = false; + rebootWatchdogFile.mechanismNextChip = xsc::Chip::NO_CHIP; + rebootWatchdogFile.mechanismNextCopy = xsc::Copy::NO_COPY; + rebootWatchdogFile.bootFlag = false; + rewriteRebootWatchdogFile(rebootWatchdogFile); } else { - if (not parseRebootFile(path, rebootFile)) { - performRebootFileHandling(true); + if (not parseRebootWatchdogFile(path, rebootWatchdogFile)) { + performRebootWatchdogHandling(true); } } if (CURRENT_CHIP == xsc::CHIP_0) { if (CURRENT_COPY == xsc::COPY_0) { - rebootFile.img00Cnt++; + rebootWatchdogFile.img00Cnt++; } else { - rebootFile.img01Cnt++; + rebootWatchdogFile.img01Cnt++; } } else { if (CURRENT_COPY == xsc::COPY_0) { - rebootFile.img10Cnt++; + rebootWatchdogFile.img10Cnt++; } else { - rebootFile.img11Cnt++; + rebootWatchdogFile.img11Cnt++; } } - if (rebootFile.bootFlag) { + if (rebootWatchdogFile.bootFlag) { // Trigger event to inform ground that a reboot was triggered - uint32_t p1 = rebootFile.lastChip << 16 | rebootFile.lastCopy; - uint32_t p2 = rebootFile.img00Cnt << 24 | rebootFile.img01Cnt << 16 | rebootFile.img10Cnt << 8 | - rebootFile.img11Cnt; + uint32_t p1 = rebootWatchdogFile.lastChip << 16 | rebootWatchdogFile.lastCopy; + uint32_t p2 = rebootWatchdogFile.img00Cnt << 24 | rebootWatchdogFile.img01Cnt << 16 | + rebootWatchdogFile.img10Cnt << 8 | rebootWatchdogFile.img11Cnt; triggerEvent(REBOOT_MECHANISM_TRIGGERED, p1, p2); // Clear the boot flag - rebootFile.bootFlag = false; + rebootWatchdogFile.bootFlag = false; } - if (rebootFile.mechanismNextChip != xsc::NO_CHIP and - rebootFile.mechanismNextCopy != xsc::NO_COPY) { - if (CURRENT_CHIP != rebootFile.mechanismNextChip or - CURRENT_COPY != rebootFile.mechanismNextCopy) { - std::string infoString = std::to_string(rebootFile.mechanismNextChip) + " " + - std::to_string(rebootFile.mechanismNextCopy); + if (rebootWatchdogFile.mechanismNextChip != xsc::NO_CHIP and + rebootWatchdogFile.mechanismNextCopy != xsc::NO_COPY) { + if (CURRENT_CHIP != rebootWatchdogFile.mechanismNextChip or + CURRENT_COPY != rebootWatchdogFile.mechanismNextCopy) { + std::string infoString = std::to_string(rebootWatchdogFile.mechanismNextChip) + " " + + std::to_string(rebootWatchdogFile.mechanismNextCopy); sif::warning << "CoreController::performRebootFileHandling: Expected to be on image " << infoString << " but currently on other image. Locking " << infoString << std::endl; // Firmware or other component might be corrupt and we are on another image then the target // image specified by the mechanism. We can't really trust the target image anymore. // Lock it for now - if (rebootFile.mechanismNextChip == xsc::CHIP_0) { - if (rebootFile.mechanismNextCopy == xsc::COPY_0) { - rebootFile.img00Lock = true; + if (rebootWatchdogFile.mechanismNextChip == xsc::CHIP_0) { + if (rebootWatchdogFile.mechanismNextCopy == xsc::COPY_0) { + rebootWatchdogFile.img00Lock = true; } else { - rebootFile.img01Lock = true; + rebootWatchdogFile.img01Lock = true; } } else { - if (rebootFile.mechanismNextCopy == xsc::COPY_0) { - rebootFile.img10Lock = true; + if (rebootWatchdogFile.mechanismNextCopy == xsc::COPY_0) { + rebootWatchdogFile.img10Lock = true; } else { - rebootFile.img11Lock = true; + rebootWatchdogFile.img11Lock = true; } } } } - rebootFile.lastChip = CURRENT_CHIP; - rebootFile.lastCopy = CURRENT_COPY; + rebootWatchdogFile.lastChip = CURRENT_CHIP; + rebootWatchdogFile.lastCopy = CURRENT_COPY; // Only reboot if the reboot functionality is enabled. // The handler will still increment the boot counts - if (rebootFile.enabled and (*rebootFile.relevantBootCnt >= rebootFile.maxCount)) { + if (rebootWatchdogFile.enabled and + (*rebootWatchdogFile.relevantBootCnt >= rebootWatchdogFile.maxCount)) { // Reboot to other image bool doReboot = false; xsc::Chip tgtChip = xsc::NO_CHIP; xsc::Copy tgtCopy = xsc::NO_COPY; - determineAndExecuteReboot(rebootFile, doReboot, tgtChip, tgtCopy); + determineAndExecuteReboot(rebootWatchdogFile, doReboot, tgtChip, tgtCopy); if (doReboot) { - rebootFile.bootFlag = true; + rebootWatchdogFile.bootFlag = true; #if OBSW_VERBOSE_LEVEL >= 1 sif::info << "Boot counter for image " << CURRENT_CHIP << " " << CURRENT_COPY << " too high. Rebooting to " << tgtChip << " " << tgtCopy << std::endl; #endif - rebootFile.mechanismNextChip = tgtChip; - rebootFile.mechanismNextCopy = tgtCopy; - rewriteRebootFile(rebootFile); + rebootWatchdogFile.mechanismNextChip = tgtChip; + rebootWatchdogFile.mechanismNextCopy = tgtCopy; + rewriteRebootWatchdogFile(rebootWatchdogFile); xsc_boot_copy(static_cast(tgtChip), static_cast(tgtCopy)); } } else { - rebootFile.mechanismNextChip = xsc::NO_CHIP; - rebootFile.mechanismNextCopy = xsc::NO_COPY; + rebootWatchdogFile.mechanismNextChip = xsc::NO_CHIP; + rebootWatchdogFile.mechanismNextCopy = xsc::NO_COPY; } - rewriteRebootFile(rebootFile); + rewriteRebootWatchdogFile(rebootWatchdogFile); } -void CoreController::determineAndExecuteReboot(RebootFile &rf, bool &needsReboot, +void CoreController::determineAndExecuteReboot(RebootWatchdogFile &rf, bool &needsReboot, xsc::Chip &tgtChip, xsc::Copy &tgtCopy) { tgtChip = xsc::CHIP_0; tgtCopy = xsc::COPY_0; @@ -217,7 +218,7 @@ void CoreController::determineAndExecuteReboot(RebootFile &rf, bool &needsReboot } } -bool CoreController::parseRebootFile(std::string path, RebootFile &rf) { +bool CoreController::parseRebootWatchdogFile(std::string path, RebootWatchdogFile &rf) { using namespace std; std::string selfMatch; if (CURRENT_CHIP == xsc::CHIP_0) { @@ -400,34 +401,34 @@ bool CoreController::parseRebootFile(std::string path, RebootFile &rf) { } void CoreController::resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy) { - std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE; + std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE; // Disable the reboot file mechanism - parseRebootFile(path, rebootFile); + parseRebootWatchdogFile(path, rebootWatchdogFile); if (tgtChip == xsc::ALL_CHIP and tgtCopy == xsc::ALL_COPY) { - rebootFile.img00Cnt = 0; - rebootFile.img01Cnt = 0; - rebootFile.img10Cnt = 0; - rebootFile.img11Cnt = 0; + rebootWatchdogFile.img00Cnt = 0; + rebootWatchdogFile.img01Cnt = 0; + rebootWatchdogFile.img10Cnt = 0; + rebootWatchdogFile.img11Cnt = 0; } else { if (tgtChip == xsc::CHIP_0) { if (tgtCopy == xsc::COPY_0) { - rebootFile.img00Cnt = 0; + rebootWatchdogFile.img00Cnt = 0; } else { - rebootFile.img01Cnt = 0; + rebootWatchdogFile.img01Cnt = 0; } } else { if (tgtCopy == xsc::COPY_0) { - rebootFile.img10Cnt = 0; + rebootWatchdogFile.img10Cnt = 0; } else { - rebootFile.img11Cnt = 0; + rebootWatchdogFile.img11Cnt = 0; } } } - rewriteRebootFile(rebootFile); + rewriteRebootWatchdogFile(rebootWatchdogFile); } -void CoreController::rewriteRebootFile(RebootFile file) { - std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE; +void CoreController::rewriteRebootWatchdogFile(RebootWatchdogFile file) { + std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE; std::ofstream rebootFile(path); if (rebootFile.is_open()) { // Initiate reboot file first. Reboot handling will be on on initialization @@ -450,15 +451,15 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_ if (size < 1) { return HasActionsIF::INVALID_PARAMETERS; } - std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE; + std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE; // Disable the reboot file mechanism - parseRebootFile(path, rebootFile); + parseRebootWatchdogFile(path, rebootWatchdogFile); if (data[0] == 0) { - rebootFile.enabled = false; - rewriteRebootFile(rebootFile); + rebootWatchdogFile.enabled = false; + rewriteRebootWatchdogFile(rebootWatchdogFile); } else if (data[0] == 1) { - rebootFile.enabled = true; - rewriteRebootFile(rebootFile); + rebootWatchdogFile.enabled = true; + rewriteRebootWatchdogFile(rebootWatchdogFile); } else { return HasActionsIF::INVALID_PARAMETERS; } @@ -490,11 +491,11 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_ if (size < 1) { return HasActionsIF::INVALID_PARAMETERS; } - std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE; + std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE; // Disable the reboot file mechanism - parseRebootFile(path, rebootFile); - rebootFile.maxCount = data[0]; - rewriteRebootFile(rebootFile); + parseRebootWatchdogFile(path, rebootWatchdogFile); + rebootWatchdogFile.maxCount = data[0]; + rewriteRebootWatchdogFile(rebootWatchdogFile); return HasActionsIF::EXECUTION_FINISHED; } default: { @@ -504,23 +505,23 @@ ReturnValue_t CoreController::executeAction(ActionId_t actionId, MessageQueueId_ } void CoreController::setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::Copy tgtCopy) { - std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_FILE; + std::string path = sdcMan->getCurrentMountPrefix(sdInfo.active) + REBOOT_WATCHDOG_FILE; // Disable the reboot file mechanism - parseRebootFile(path, rebootFile); + parseRebootWatchdogFile(path, rebootWatchdogFile); if (tgtChip == xsc::CHIP_0) { if (tgtCopy == xsc::COPY_0) { - rebootFile.img00Lock = lock; + rebootWatchdogFile.img00Lock = lock; } else { - rebootFile.img01Lock = lock; + rebootWatchdogFile.img01Lock = lock; } } else { if (tgtCopy == xsc::COPY_0) { - rebootFile.img10Lock = lock; + rebootWatchdogFile.img10Lock = lock; } else { - rebootFile.img11Lock = lock; + rebootWatchdogFile.img11Lock = lock; } } - rewriteRebootFile(rebootFile); + rewriteRebootWatchdogFile(rebootWatchdogFile); } void CoreController::setCurrentBootCopy(xsc::Chip chip, xsc::Copy copy) { diff --git a/unittest/rebootLogic/src/CoreController.h b/unittest/rebootLogic/src/CoreController.h index 1846c27f..34d2a5f7 100644 --- a/unittest/rebootLogic/src/CoreController.h +++ b/unittest/rebootLogic/src/CoreController.h @@ -14,7 +14,7 @@ enum Copy : int { COPY_0, COPY_1, NO_COPY, SELF_COPY, ALL_COPY }; } // namespace xsc -struct RebootFile { +struct RebootWatchdogFile { static constexpr uint8_t DEFAULT_MAX_BOOT_CNT = 10; bool enabled = true; @@ -39,7 +39,7 @@ class SdCardManager; class CoreController { public: - static constexpr char REBOOT_FILE[] = "/conf/reboot.txt"; + static constexpr char REBOOT_WATCHDOG_FILE[] = "/conf/reboot.txt"; //! [EXPORT] : [COMMENT] The reboot mechanism was triggered. //! P1: First 16 bits: Last Chip, Last 16 bits: Last Copy, //! P2: Each byte is the respective reboot count for the slots @@ -57,13 +57,13 @@ class CoreController { static void setCurrentBootCopy(xsc::Chip chip, xsc::Copy copy); ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy, const uint8_t* data, size_t size); - void performRebootFileHandling(bool recreateFile); - void determineAndExecuteReboot(RebootFile& rf, bool& needsReboot, xsc::Chip& tgtChip, + void performRebootWatchdogHandling(bool recreateFile); + void determineAndExecuteReboot(RebootWatchdogFile& rf, bool& needsReboot, xsc::Chip& tgtChip, xsc::Copy& tgtCopy); void setRebootMechanismLock(bool lock, xsc::Chip tgtChip, xsc::Copy tgtCopy); void resetRebootCount(xsc::Chip tgtChip, xsc::Copy tgtCopy); - bool parseRebootFile(std::string path, RebootFile& file); - void rewriteRebootFile(RebootFile file); + bool parseRebootWatchdogFile(std::string path, RebootWatchdogFile& file); + void rewriteRebootWatchdogFile(RebootWatchdogFile file); private: struct SdFsmParams { @@ -74,6 +74,6 @@ class CoreController { } sdInfo; SdCardManager* sdcMan = nullptr; - RebootFile rebootFile = {}; + RebootWatchdogFile rebootWatchdogFile = {}; bool doPerformRebootFileHandling = true; }; \ No newline at end of file diff --git a/unittest/testEnvironment.cpp b/unittest/testEnvironment.cpp index 72726f39..4c19c4cf 100644 --- a/unittest/testEnvironment.cpp +++ b/unittest/testEnvironment.cpp @@ -27,7 +27,7 @@ void factory(void* args) { new HouseKeepingMock(); eventManager = new EventManagerMock(); new HealthTable(objects::HEALTH_TABLE); - new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER); + new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER, 5, false, 60.0); new CdsShortTimeStamper(objects::TIME_STAMPER); { diff --git a/unittest/testGenericFilesystem.cpp b/unittest/testGenericFilesystem.cpp new file mode 100644 index 00000000..e5279bab --- /dev/null +++ b/unittest/testGenericFilesystem.cpp @@ -0,0 +1,43 @@ + +#include +#include + +#include "fsfw/timemanager/Clock.h" + +uint8_t extractSuffix(const std::string& pathStr) { + std::string numberStr; + // Find the position of the dot at the end of the file path + size_t dotPos = pathStr.find_last_of('.'); + if (dotPos != std::string::npos && dotPos < pathStr.length() - 1) { + // Extract the substring after the dot + numberStr = pathStr.substr(dotPos + 1); + } + int number = std::stoi(numberStr); + if (number < 0 or number > std::numeric_limits::max()) { + return 0; + } + return static_cast(number); +} + +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); +} + +TEST_CASE("Suffix Extraction") { + std::string pathStr = "/mnt/sd0/tm/hk/hk-some-stamp.bin.0"; + CHECK(extractSuffix(pathStr) == 0); + pathStr = "/mnt/sd0/tm/hk/hk-some-stamp.bin.2"; + CHECK(extractSuffix(pathStr) == 2); +} diff --git a/unittest/testStampInFilename.cpp b/unittest/testStampInFilename.cpp deleted file mode 100644 index c66bdce6..00000000 --- a/unittest/testStampInFilename.cpp +++ /dev/null @@ -1,21 +0,0 @@ - -#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); -}