diff --git a/bsp_q7s/OBSWConfig.h.in b/bsp_q7s/OBSWConfig.h.in index 260ccc2d..f3cfa907 100644 --- a/bsp_q7s/OBSWConfig.h.in +++ b/bsp_q7s/OBSWConfig.h.in @@ -20,7 +20,6 @@ // Set to 1 if telecommands are received via the PDEC IP Core #define OBSW_TC_FROM_PDEC 0 -#define OBSW_ENABLE_TIMERS 1 #define OBSW_ADD_GOMSPACE_PCDU @OBSW_ADD_GOMSPACE_PCDU@ #define OBSW_ADD_MGT @OBSW_ADD_MGT@ #define OBSW_ADD_BPX_BATTERY_HANDLER @OBSW_ADD_BPX_BATTERY_HANDLER@ @@ -41,7 +40,11 @@ #define OBSW_ADD_PL_PCDU @OBSW_ADD_PL_PCDU@ #define OBSW_ADD_SYRLINKS @OBSW_ADD_SYRLINKS@ #define OBSW_ENABLE_SYRLINKS_TRANSMIT_TIMEOUT 0 -#define OBSW_MPSOC_JTAG_BOOT 0 + +// Configuration parameter which causes the core controller to try to keep at least one SD card +// working +#define OBSW_SD_CARD_MUST_BE_ON 1 +#define OBSW_ENABLE_TIMERS 1 // This is a really tricky switch.. It initializes the PCDU switches to their default states // at powerup. I think it would be better @@ -59,6 +62,7 @@ #define OBSW_SWITCH_TO_NORMAL_MODE_AFTER_STARTUP 1 #define OBSW_PRINT_MISSED_DEADLINES 1 +#define OBSW_MPSOC_JTAG_BOOT 0 #define OBSW_STAR_TRACKER_GROUND_CONFIG 1 #define OBSW_SYRLINKS_SIMULATED 1 #define OBSW_ADD_TEST_CODE 0 diff --git a/bsp_q7s/core/CoreController.cpp b/bsp_q7s/core/CoreController.cpp index 30d5228d..95700148 100644 --- a/bsp_q7s/core/CoreController.cpp +++ b/bsp_q7s/core/CoreController.cpp @@ -4,7 +4,6 @@ #include #include -#include "OBSWConfig.h" #include "OBSWVersion.h" #include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/timemanager/Stopwatch.h" @@ -1243,16 +1242,16 @@ void CoreController::performMountedSdCardOperations() { } timeFileHandler(); }; - bool clearOneShotFlag = false; + bool someSdCardActive = false; if (sdInfo.active == sd::SdCard::SLOT_0 and sdcMan->isSdCardUsable(sd::SdCard::SLOT_0)) { mountedSdCardOp(sd::SdCard::SLOT_0, config::SD_0_MOUNT_POINT); - clearOneShotFlag = true; + someSdCardActive = true; } if (sdInfo.active == sd::SdCard::SLOT_1 and sdcMan->isSdCardUsable(sd::SdCard::SLOT_1)) { mountedSdCardOp(sd::SdCard::SLOT_1, config::SD_1_MOUNT_POINT); - clearOneShotFlag = true; + someSdCardActive = true; } - if (clearOneShotFlag) { + if (someSdCardActive) { performOneShotSdCardOpsSwitch = true; } } @@ -1295,7 +1294,19 @@ ReturnValue_t CoreController::performSdCardCheck() { if (active.second == sd::SdState::MOUNTED) { sdCardCheck(sd::SdCard::SLOT_1); } - +#if OBSW_SD_CARD_MUST_BE_ON == 1 + // This is FDIR. The core controller will attempt once to get some SD card working + bool someSdCardActive = false; + if ((sdInfo.active == sd::SdCard::SLOT_0 and sdcMan->isSdCardUsable(sd::SdCard::SLOT_0)) or + (sdInfo.active == sd::SdCard::SLOT_1 and sdcMan->isSdCardUsable(sd::SdCard::SLOT_1))) { + someSdCardActive = true; + } + if (not someSdCardActive and remountAttemptFlag) { + triggerEvent(NO_SD_CARD_ACTIVE); + initSdCardBlocking(); + remountAttemptFlag = false; + } +#endif return returnvalue::OK; } diff --git a/bsp_q7s/core/CoreController.h b/bsp_q7s/core/CoreController.h index 3c205e4a..460c0f8a 100644 --- a/bsp_q7s/core/CoreController.h +++ b/bsp_q7s/core/CoreController.h @@ -7,6 +7,7 @@ #include #include "CoreDefinitions.h" +#include "OBSWConfig.h" #include "bsp_q7s/fs/SdCardManager.h" #include "events/subsystemIdRanges.h" #include "fsfw/controller/ExtendedControllerBase.h" @@ -102,6 +103,9 @@ class CoreController : public ExtendedControllerBase { event::makeEvent(SUBSYSTEM_ID, 2, severity::MEDIUM); //! Trying to find a way how to determine that the reboot came from ProASIC3 or PCDU.. static constexpr Event REBOOT_HW = event::makeEvent(SUBSYSTEM_ID, 3, severity::MEDIUM); + //! [EXPORT] : [COMMENT] No SD card was active. Core controller will attempt to re-initialize + //! a SD card. + static constexpr Event NO_SD_CARD_ACTIVE = event::makeEvent(SUBSYSTEM_ID, 4, severity::HIGH); CoreController(object_id_t objectId); virtual ~CoreController(); @@ -219,6 +223,10 @@ class CoreController : public ExtendedControllerBase { core::HkSet hkSet; +#if OBSW_SD_CARD_MUST_BE_ON == 1 + bool remountAttemptFlag = true; +#endif + ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, LocalDataPoolManager& poolManager) override; Countdown sdCardCheckCd = Countdown(120000); diff --git a/bsp_q7s/core/InitMission.cpp b/bsp_q7s/core/InitMission.cpp index a5cc3df9..df36df33 100644 --- a/bsp_q7s/core/InitMission.cpp +++ b/bsp_q7s/core/InitMission.cpp @@ -67,16 +67,30 @@ void initmission::initTasks() { void (*missedDeadlineFunc)(void) = nullptr; #endif - PeriodicTaskIF* coreController = factory->createPeriodicTask( + PeriodicTaskIF* sysCtrlTask = factory->createPeriodicTask( "CORE_CTRL", 60, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.4, missedDeadlineFunc); - result = coreController->addComponent(objects::CORE_CONTROLLER); + result = sysCtrlTask->addComponent(objects::CORE_CONTROLLER); if (result != returnvalue::OK) { initmission::printAddObjectError("CORE_CTRL", objects::CORE_CONTROLLER); } + // Could add this to the core controller but the core controller does so many thing that I would + // prefer to have the solar array deployment in a seprate task. + PeriodicTaskIF* solarArrayDeplTask = factory->createPeriodicTask( + "SOLAR_ARRAY_DEPL", 65, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.4, missedDeadlineFunc); + result = solarArrayDeplTask->addComponent(objects::SOLAR_ARRAY_DEPL_HANDLER); + if (result != returnvalue::OK) { + initmission::printAddObjectError("SOLAR_ARRAY_DEPL", objects::SOLAR_ARRAY_DEPL_HANDLER); + } /* TMTC Distribution */ PeriodicTaskIF* tmTcDistributor = factory->createPeriodicTask( - "DIST", 40, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.2, missedDeadlineFunc); + "DIST", 45, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.2, missedDeadlineFunc); +#if OBSW_ADD_TCPIP_BRIDGE == 1 + result = tmTcDistributor->addComponent(objects::TMTC_BRIDGE); + if (result != returnvalue::OK) { + initmission::printAddObjectError("TMTC_BRIDGE", objects::TMTC_BRIDGE); + } +#endif result = tmTcDistributor->addComponent(objects::CCSDS_PACKET_DISTRIBUTOR); if (result != returnvalue::OK) { initmission::printAddObjectError("CCSDS_DISTRIB", objects::CCSDS_PACKET_DISTRIBUTOR); @@ -95,13 +109,6 @@ void initmission::initTasks() { } #if OBSW_ADD_TCPIP_BRIDGE == 1 - // TMTC bridge - PeriodicTaskIF* tmtcBridgeTask = factory->createPeriodicTask( - "TCPIP_TMTC_BRIDGE", 50, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.2, missedDeadlineFunc); - result = tmtcBridgeTask->addComponent(objects::TMTC_BRIDGE); - if (result != returnvalue::OK) { - initmission::printAddObjectError("TMTC_BRIDGE", objects::TMTC_BRIDGE); - } PeriodicTaskIF* tmtcPollingTask = factory->createPeriodicTask( "TMTC_POLLING", 80, PeriodicTaskIF::MINIMUM_STACK_SIZE, 2.0, missedDeadlineFunc); result = tmtcPollingTask->addComponent(objects::TMTC_POLLING_TASK); @@ -138,39 +145,39 @@ void initmission::initTasks() { } #endif - PeriodicTaskIF* acsTask = factory->createPeriodicTask( + PeriodicTaskIF* acsCtrlTask = factory->createPeriodicTask( "ACS_TASK", 50, PeriodicTaskIF::MINIMUM_STACK_SIZE * 2, 0.4, missedDeadlineFunc); #if OBSW_ADD_GPS_CTRL == 1 - result = acsTask->addComponent(objects::GPS_CONTROLLER); + result = acsCtrlTask->addComponent(objects::GPS_CONTROLLER); if (result != returnvalue::OK) { initmission::printAddObjectError("GPS_CTRL", objects::GPS_CONTROLLER); } #endif /* OBSW_ADD_GPS_CTRL */ #if OBSW_ADD_ACS_CTRL == 1 - acsTask->addComponent(objects::ACS_CONTROLLER); + acsCtrlTask->addComponent(objects::ACS_CONTROLLER); if (result != returnvalue::OK) { initmission::printAddObjectError("ACS_CTRL", objects::ACS_CONTROLLER); } #endif - PeriodicTaskIF* sysTask = factory->createPeriodicTask( + PeriodicTaskIF* acsSysTask = factory->createPeriodicTask( "SYS_TASK", 40, PeriodicTaskIF::MINIMUM_STACK_SIZE * 2, 0.4, missedDeadlineFunc); - static_cast(sysTask); + static_cast(acsSysTask); #if OBSW_ADD_ACS_BOARD == 1 - result = sysTask->addComponent(objects::ACS_BOARD_ASS); + result = acsSysTask->addComponent(objects::ACS_BOARD_ASS); if (result != returnvalue::OK) { initmission::printAddObjectError("ACS_BOARD_ASS", objects::ACS_BOARD_ASS); } #endif /* OBSW_ADD_ACS_HANDLERS */ #if OBSW_ADD_RW == 1 - result = sysTask->addComponent(objects::RW_ASS); + result = acsSysTask->addComponent(objects::RW_ASS); if (result != returnvalue::OK) { initmission::printAddObjectError("RW_ASS", objects::RW_ASS); } #endif #if OBSW_ADD_SUS_BOARD_ASS == 1 - result = sysTask->addComponent(objects::SUS_BOARD_ASS); + result = acsSysTask->addComponent(objects::SUS_BOARD_ASS); if (result != returnvalue::OK) { initmission::printAddObjectError("SUS_BOARD_ASS", objects::SUS_BOARD_ASS); } @@ -211,13 +218,27 @@ void initmission::initTasks() { tcsTask->addComponent(rtd, DeviceHandlerIF::SEND_READ); tcsTask->addComponent(rtd, DeviceHandlerIF::GET_READ); } - tcsTask->addComponent(objects::TCS_BOARD_ASS); -#endif /* OBSW_ADD_RTD_DEVICES */ - -#if OBSW_ADD_TCS_CTRL == 1 - tcsTask->addComponent(objects::THERMAL_CONTROLLER); #endif + PeriodicTaskIF* tcsSystemTask = factory->createPeriodicTask( + "TCS_TASK", 45, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.5, missedDeadlineFunc); +#if OBSW_ADD_RTD_DEVICES == 1 + result = tcsSystemTask->addComponent(objects::TCS_BOARD_ASS); + if (result != returnvalue::OK) { + initmission::printAddObjectError("TCS_BOARD_ASS", objects::TCS_BOARD_ASS); + } +#endif /* OBSW_ADD_RTD_DEVICES */ +#if OBSW_ADD_TCS_CTRL == 1 + result = tcsSystemTask->addComponent(objects::THERMAL_CONTROLLER); + if (result != returnvalue::OK) { + initmission::printAddObjectError("THERMAL_CONTROLLER", objects::THERMAL_CONTROLLER); + } +#endif + result = tcsSystemTask->addComponent(objects::HEATER_HANDLER); + if (result != returnvalue::OK) { + initmission::printAddObjectError("HEATER_HANDLER", objects::HEATER_HANDLER); + } + #if OBSW_ADD_STAR_TRACKER == 1 PeriodicTaskIF* strHelperTask = factory->createPeriodicTask( "STR_HELPER", 20, PeriodicTaskIF::MINIMUM_STACK_SIZE, 0.2, missedDeadlineFunc); @@ -283,7 +304,6 @@ void initmission::initTasks() { tmTcDistributor->startTask(); #if OBSW_ADD_TCPIP_BRIDGE == 1 - tmtcBridgeTask->startTask(); tmtcPollingTask->startTask(); #endif @@ -292,7 +312,8 @@ void initmission::initTasks() { pdecHandlerTask->startTask(); #endif /* OBSW_USE_CCSDS_IP_CORE == 1 */ - coreController->startTask(); + sysCtrlTask->startTask(); + solarArrayDeplTask->startTask(); taskStarter(pstTasks, "PST task vector"); taskStarter(pusTasks, "PUS task vector"); @@ -313,12 +334,13 @@ void initmission::initTasks() { strHelperTask->startTask(); #endif /* OBSW_ADD_STAR_TRACKER == 1 */ - acsTask->startTask(); - sysTask->startTask(); + acsCtrlTask->startTask(); + acsSysTask->startTask(); #if OBSW_ADD_RTD_DEVICES == 1 tcsPollingTask->startTask(); tcsTask->startTask(); #endif /* OBSW_ADD_RTD_DEVICES == 1 */ + tcsSystemTask->startTask(); #if OBSW_ADD_PLOC_SUPERVISOR == 1 supvHelperTask->startTask(); #endif /* OBSW_ADD_PLOC_SUPERVISOR == 1 */ @@ -378,18 +400,6 @@ void initmission::createPstTasks(TaskFactory& factory, taskVec.push_back(uartPst); } - FixedTimeslotTaskIF* gpioPst = factory.createFixedTimeslotTask( - "GPIO_PST", 70, PeriodicTaskIF::MINIMUM_STACK_SIZE * 2, 0.2, missedDeadlineFunc); - result = pst::pstGpio(gpioPst); - if (result != returnvalue::OK) { - if (result == FixedTimeslotTaskIF::SLOT_LIST_EMPTY) { - sif::warning << "InitMission::initTasks: GPIO PST is empty" << std::endl; - } else { - sif::error << "InitMission::initTasks: Creating GPIO PST failed!" << std::endl; - } - } else { - taskVec.push_back(gpioPst); - } #if OBSW_ADD_I2C_TEST_CODE == 0 FixedTimeslotTaskIF* i2cPst = factory.createFixedTimeslotTask( "I2C_PST", 65, PeriodicTaskIF::MINIMUM_STACK_SIZE * 4, 0.2, missedDeadlineFunc); diff --git a/bsp_q7s/core/ObjectFactory.cpp b/bsp_q7s/core/ObjectFactory.cpp index 80dcb55e..74af9564 100644 --- a/bsp_q7s/core/ObjectFactory.cpp +++ b/bsp_q7s/core/ObjectFactory.cpp @@ -539,7 +539,8 @@ void ObjectFactory::createHeaterComponents(GpioIF* gpioIF, PowerSwitchIF* pwrSwi pcdu::Switches::PDU2_CH3_TCS_BOARD_HEATER_IN_8V); } -void ObjectFactory::createSolarArrayDeploymentComponents() { +void ObjectFactory::createSolarArrayDeploymentComponents(PowerSwitchIF& pwrSwitcher, + GpioIF& gpioIF) { using namespace gpio; GpioCookie* solarArrayDeplCookie = new GpioCookie; GpiodRegularByLineName* gpio = nullptr; @@ -552,12 +553,14 @@ void ObjectFactory::createSolarArrayDeploymentComponents() { gpio = new GpiodRegularByLineName(q7s::gpioNames::SA_DPL_PIN_1, consumer.str(), Direction::OUT, Levels::LOW); solarArrayDeplCookie->addGpio(gpioIds::DEPLSA2, gpio); + ReturnValue_t result = gpioIF.addGpios(solarArrayDeplCookie); + if (result != returnvalue::OK) { + sif::error << "Adding Solar Array Deployment GPIO cookie failed" << std::endl; + } - // TODO: Find out burn time. For now set to 1000 ms. - new SolarArrayDeploymentHandler(objects::SOLAR_ARRAY_DEPL_HANDLER, objects::GPIO_IF, - solarArrayDeplCookie, objects::PCDU_HANDLER, + new SolarArrayDeploymentHandler(objects::SOLAR_ARRAY_DEPL_HANDLER, gpioIF, pwrSwitcher, pcdu::Switches::PDU2_CH5_DEPLOYMENT_MECHANISM_8V, - gpioIds::DEPLSA1, gpioIds::DEPLSA2, 1000); + gpioIds::DEPLSA1, gpioIds::DEPLSA2, *SdCardManager::instance()); } void ObjectFactory::createSyrlinksComponents(PowerSwitchIF* pwrSwitcher) { diff --git a/bsp_q7s/core/ObjectFactory.h b/bsp_q7s/core/ObjectFactory.h index 0b81c235..88881090 100644 --- a/bsp_q7s/core/ObjectFactory.h +++ b/bsp_q7s/core/ObjectFactory.h @@ -33,7 +33,7 @@ void createHeaterComponents(GpioIF* gpioIF, PowerSwitchIF* pwrSwitcher, HealthTa void createImtqComponents(PowerSwitchIF* pwrSwitcher); void createBpxBatteryComponent(); void createStrComponents(PowerSwitchIF* pwrSwitcher); -void createSolarArrayDeploymentComponents(); +void createSolarArrayDeploymentComponents(PowerSwitchIF& pwrSwitcher, GpioIF& gpioIF); void createSyrlinksComponents(PowerSwitchIF* pwrSwitcher); void createPayloadComponents(LinuxLibgpioIF* gpioComIF); void createReactionWheelComponents(LinuxLibgpioIF* gpioComIF, PowerSwitchIF* pwrSwitcher); diff --git a/bsp_q7s/fmObjectFactory.cpp b/bsp_q7s/fmObjectFactory.cpp index 1746c048..d28b7248 100644 --- a/bsp_q7s/fmObjectFactory.cpp +++ b/bsp_q7s/fmObjectFactory.cpp @@ -35,7 +35,7 @@ void ObjectFactory::produce(void* args) { createAcsBoardComponents(gpioComIF, uartComIF, pwrSwitcher); #endif createHeaterComponents(gpioComIF, pwrSwitcher, healthTable); - createSolarArrayDeploymentComponents(); + createSolarArrayDeploymentComponents(*pwrSwitcher, *gpioComIF); createPlPcduComponents(gpioComIF, spiMainComIF, pwrSwitcher); #if OBSW_ADD_SYRLINKS == 1 createSyrlinksComponents(pwrSwitcher); diff --git a/common/config/eive/definitions.h b/common/config/eive/definitions.h index 1e8faad9..33d37812 100644 --- a/common/config/eive/definitions.h +++ b/common/config/eive/definitions.h @@ -34,6 +34,15 @@ static constexpr uint8_t LIVE_TM = 0; static constexpr uint32_t MAX_PATH_SIZE = 100; static constexpr uint32_t MAX_FILENAME_SIZE = 50; +static constexpr uint32_t SA_DEPL_INIT_BUFFER_SECS = 120; +// Burn time for autonomous deployment +static constexpr uint32_t SA_DEPL_BURN_TIME_SECS = 180; +static constexpr uint32_t SA_DEPL_WAIT_TIME_SECS = 45 * 60; +// HW constraints (current limit) mean that the GPIO channels need to be switched on in alternation +static constexpr uint32_t SA_DEPL_CHANNEL_ALTERNATION_INTERVAL_SECS = 5; +// Maximum allowed burn time allowed by the software. +static constexpr uint32_t SA_DEPL_MAX_BURN_TIME = 180; + } // namespace config #endif /* COMMON_CONFIG_DEFINITIONS_H_ */ diff --git a/generators/bsp_q7s_events.csv b/generators/bsp_q7s_events.csv index 49cdc610..5a662fb5 100644 --- a/generators/bsp_q7s_events.csv +++ b/generators/bsp_q7s_events.csv @@ -92,11 +92,15 @@ Event ID (dec); Event ID (hex); Name; Severity; Description; File Path 11405;0x2c8d;SWITCH_ALREADY_OFF;LOW;;mission/devices/HeaterHandler.h 11406;0x2c8e;MAIN_SWITCH_TIMEOUT;MEDIUM;;mission/devices/HeaterHandler.h 11407;0x2c8f;FAULTY_HEATER_WAS_ON;LOW;;mission/devices/HeaterHandler.h -11500;0x2cec;MAIN_SWITCH_ON_TIMEOUT;LOW;;mission/devices/SolarArrayDeploymentHandler.h -11501;0x2ced;MAIN_SWITCH_OFF_TIMEOUT;LOW;;mission/devices/SolarArrayDeploymentHandler.h -11502;0x2cee;DEPLOYMENT_FAILED;HIGH;;mission/devices/SolarArrayDeploymentHandler.h -11503;0x2cef;DEPL_SA1_GPIO_SWTICH_ON_FAILED;HIGH;;mission/devices/SolarArrayDeploymentHandler.h -11504;0x2cf0;DEPL_SA2_GPIO_SWTICH_ON_FAILED;HIGH;;mission/devices/SolarArrayDeploymentHandler.h +11500;0x2cec;BURN_PHASE_START;INFO;P1: Burn duration in milliseconds, P2: Dry run flag;mission/devices/SolarArrayDeploymentHandler.h +11501;0x2ced;BURN_PHASE_DONE;INFO;P1: Burn duration in milliseconds, P2: Dry run flag;mission/devices/SolarArrayDeploymentHandler.h +11502;0x2cee;MAIN_SWITCH_ON_TIMEOUT;LOW;;mission/devices/SolarArrayDeploymentHandler.h +11503;0x2cef;MAIN_SWITCH_OFF_TIMEOUT;LOW;;mission/devices/SolarArrayDeploymentHandler.h +11504;0x2cf0;DEPL_SA1_GPIO_SWTICH_ON_FAILED;HIGH;;mission/devices/SolarArrayDeploymentHandler.h +11505;0x2cf1;DEPL_SA2_GPIO_SWTICH_ON_FAILED;HIGH;;mission/devices/SolarArrayDeploymentHandler.h +11506;0x2cf2;DEPL_SA1_GPIO_SWTICH_OFF_FAILED;HIGH;;mission/devices/SolarArrayDeploymentHandler.h +11507;0x2cf3;DEPL_SA2_GPIO_SWTICH_OFF_FAILED;HIGH;;mission/devices/SolarArrayDeploymentHandler.h +11508;0x2cf4;AUTONOMOUS_DEPLOYMENT_COMPLETED;INFO;;mission/devices/SolarArrayDeploymentHandler.h 11601;0x2d51;MEMORY_READ_RPT_CRC_FAILURE;LOW;PLOC crc failure in telemetry packet;linux/devices/ploc/PlocMPSoCHandler.h 11602;0x2d52;ACK_FAILURE;LOW;PLOC receive acknowledgment failure report P1: Command Id which leads the acknowledgment failure report P2: The status field inserted by the MPSoC into the data field;linux/devices/ploc/PlocMPSoCHandler.h 11603;0x2d53;EXE_FAILURE;LOW;PLOC receive execution failure report P1: Command Id which leads the execution failure report P2: The status field inserted by the MPSoC into the data field;linux/devices/ploc/PlocMPSoCHandler.h diff --git a/generators/events/translateEvents.cpp b/generators/events/translateEvents.cpp index 8711750b..b27c54fc 100644 --- a/generators/events/translateEvents.cpp +++ b/generators/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** - * @brief Auto-generated event translation file. Contains 225 translations. + * @brief Auto-generated event translation file. Contains 229 translations. * @details - * Generated on: 2022-10-10 11:15:49 + * Generated on: 2022-10-14 14:54:47 */ #include "translateEvents.h" @@ -98,11 +98,15 @@ const char *SWITCH_ALREADY_ON_STRING = "SWITCH_ALREADY_ON"; const char *SWITCH_ALREADY_OFF_STRING = "SWITCH_ALREADY_OFF"; const char *MAIN_SWITCH_TIMEOUT_STRING = "MAIN_SWITCH_TIMEOUT"; const char *FAULTY_HEATER_WAS_ON_STRING = "FAULTY_HEATER_WAS_ON"; +const char *BURN_PHASE_START_STRING = "BURN_PHASE_START"; +const char *BURN_PHASE_DONE_STRING = "BURN_PHASE_DONE"; const char *MAIN_SWITCH_ON_TIMEOUT_STRING = "MAIN_SWITCH_ON_TIMEOUT"; const char *MAIN_SWITCH_OFF_TIMEOUT_STRING = "MAIN_SWITCH_OFF_TIMEOUT"; -const char *DEPLOYMENT_FAILED_STRING = "DEPLOYMENT_FAILED"; const char *DEPL_SA1_GPIO_SWTICH_ON_FAILED_STRING = "DEPL_SA1_GPIO_SWTICH_ON_FAILED"; const char *DEPL_SA2_GPIO_SWTICH_ON_FAILED_STRING = "DEPL_SA2_GPIO_SWTICH_ON_FAILED"; +const char *DEPL_SA1_GPIO_SWTICH_OFF_FAILED_STRING = "DEPL_SA1_GPIO_SWTICH_OFF_FAILED"; +const char *DEPL_SA2_GPIO_SWTICH_OFF_FAILED_STRING = "DEPL_SA2_GPIO_SWTICH_OFF_FAILED"; +const char *AUTONOMOUS_DEPLOYMENT_COMPLETED_STRING = "AUTONOMOUS_DEPLOYMENT_COMPLETED"; const char *MEMORY_READ_RPT_CRC_FAILURE_STRING = "MEMORY_READ_RPT_CRC_FAILURE"; const char *ACK_FAILURE_STRING = "ACK_FAILURE"; const char *EXE_FAILURE_STRING = "EXE_FAILURE"; @@ -416,15 +420,23 @@ const char *translateEvents(Event event) { case (11407): return FAULTY_HEATER_WAS_ON_STRING; case (11500): - return MAIN_SWITCH_ON_TIMEOUT_STRING; + return BURN_PHASE_START_STRING; case (11501): - return MAIN_SWITCH_OFF_TIMEOUT_STRING; + return BURN_PHASE_DONE_STRING; case (11502): - return DEPLOYMENT_FAILED_STRING; + return MAIN_SWITCH_ON_TIMEOUT_STRING; case (11503): - return DEPL_SA1_GPIO_SWTICH_ON_FAILED_STRING; + return MAIN_SWITCH_OFF_TIMEOUT_STRING; case (11504): + return DEPL_SA1_GPIO_SWTICH_ON_FAILED_STRING; + case (11505): return DEPL_SA2_GPIO_SWTICH_ON_FAILED_STRING; + case (11506): + return DEPL_SA1_GPIO_SWTICH_OFF_FAILED_STRING; + case (11507): + return DEPL_SA2_GPIO_SWTICH_OFF_FAILED_STRING; + case (11508): + return AUTONOMOUS_DEPLOYMENT_COMPLETED_STRING; case (11601): return MEMORY_READ_RPT_CRC_FAILURE_STRING; case (11602): diff --git a/generators/objects/translateObjects.cpp b/generators/objects/translateObjects.cpp index 910fe8fd..a855e48a 100644 --- a/generators/objects/translateObjects.cpp +++ b/generators/objects/translateObjects.cpp @@ -2,7 +2,7 @@ * @brief Auto-generated object translation file. * @details * Contains 138 translations. - * Generated on: 2022-10-10 11:15:49 + * Generated on: 2022-10-14 14:54:47 */ #include "translateObjects.h" diff --git a/linux/fsfwconfig/events/translateEvents.cpp b/linux/fsfwconfig/events/translateEvents.cpp index 8711750b..b27c54fc 100644 --- a/linux/fsfwconfig/events/translateEvents.cpp +++ b/linux/fsfwconfig/events/translateEvents.cpp @@ -1,7 +1,7 @@ /** - * @brief Auto-generated event translation file. Contains 225 translations. + * @brief Auto-generated event translation file. Contains 229 translations. * @details - * Generated on: 2022-10-10 11:15:49 + * Generated on: 2022-10-14 14:54:47 */ #include "translateEvents.h" @@ -98,11 +98,15 @@ const char *SWITCH_ALREADY_ON_STRING = "SWITCH_ALREADY_ON"; const char *SWITCH_ALREADY_OFF_STRING = "SWITCH_ALREADY_OFF"; const char *MAIN_SWITCH_TIMEOUT_STRING = "MAIN_SWITCH_TIMEOUT"; const char *FAULTY_HEATER_WAS_ON_STRING = "FAULTY_HEATER_WAS_ON"; +const char *BURN_PHASE_START_STRING = "BURN_PHASE_START"; +const char *BURN_PHASE_DONE_STRING = "BURN_PHASE_DONE"; const char *MAIN_SWITCH_ON_TIMEOUT_STRING = "MAIN_SWITCH_ON_TIMEOUT"; const char *MAIN_SWITCH_OFF_TIMEOUT_STRING = "MAIN_SWITCH_OFF_TIMEOUT"; -const char *DEPLOYMENT_FAILED_STRING = "DEPLOYMENT_FAILED"; const char *DEPL_SA1_GPIO_SWTICH_ON_FAILED_STRING = "DEPL_SA1_GPIO_SWTICH_ON_FAILED"; const char *DEPL_SA2_GPIO_SWTICH_ON_FAILED_STRING = "DEPL_SA2_GPIO_SWTICH_ON_FAILED"; +const char *DEPL_SA1_GPIO_SWTICH_OFF_FAILED_STRING = "DEPL_SA1_GPIO_SWTICH_OFF_FAILED"; +const char *DEPL_SA2_GPIO_SWTICH_OFF_FAILED_STRING = "DEPL_SA2_GPIO_SWTICH_OFF_FAILED"; +const char *AUTONOMOUS_DEPLOYMENT_COMPLETED_STRING = "AUTONOMOUS_DEPLOYMENT_COMPLETED"; const char *MEMORY_READ_RPT_CRC_FAILURE_STRING = "MEMORY_READ_RPT_CRC_FAILURE"; const char *ACK_FAILURE_STRING = "ACK_FAILURE"; const char *EXE_FAILURE_STRING = "EXE_FAILURE"; @@ -416,15 +420,23 @@ const char *translateEvents(Event event) { case (11407): return FAULTY_HEATER_WAS_ON_STRING; case (11500): - return MAIN_SWITCH_ON_TIMEOUT_STRING; + return BURN_PHASE_START_STRING; case (11501): - return MAIN_SWITCH_OFF_TIMEOUT_STRING; + return BURN_PHASE_DONE_STRING; case (11502): - return DEPLOYMENT_FAILED_STRING; + return MAIN_SWITCH_ON_TIMEOUT_STRING; case (11503): - return DEPL_SA1_GPIO_SWTICH_ON_FAILED_STRING; + return MAIN_SWITCH_OFF_TIMEOUT_STRING; case (11504): + return DEPL_SA1_GPIO_SWTICH_ON_FAILED_STRING; + case (11505): return DEPL_SA2_GPIO_SWTICH_ON_FAILED_STRING; + case (11506): + return DEPL_SA1_GPIO_SWTICH_OFF_FAILED_STRING; + case (11507): + return DEPL_SA2_GPIO_SWTICH_OFF_FAILED_STRING; + case (11508): + return AUTONOMOUS_DEPLOYMENT_COMPLETED_STRING; case (11601): return MEMORY_READ_RPT_CRC_FAILURE_STRING; case (11602): diff --git a/linux/fsfwconfig/objects/translateObjects.cpp b/linux/fsfwconfig/objects/translateObjects.cpp index 910fe8fd..a855e48a 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 138 translations. - * Generated on: 2022-10-10 11:15:49 + * Generated on: 2022-10-14 14:54:47 */ #include "translateObjects.h" diff --git a/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.cpp b/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.cpp index e11632c8..0d75588f 100644 --- a/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.cpp +++ b/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.cpp @@ -15,22 +15,6 @@ #define RPI_TEST_GPS_HANDLER 0 #endif -ReturnValue_t pst::pstGpio(FixedTimeslotTaskIF *thisSequence) { - // Length of a communication cycle - uint32_t length = thisSequence->getPeriodMs(); - - thisSequence->addSlot(objects::HEATER_HANDLER, length * 0, DeviceHandlerIF::PERFORM_OPERATION); - thisSequence->addSlot(objects::SOLAR_ARRAY_DEPL_HANDLER, length * 0, - DeviceHandlerIF::PERFORM_OPERATION); - - if (thisSequence->checkSequence() == returnvalue::OK) { - return returnvalue::OK; - } - - sif::error << "PollingSequence::initialize has errors!" << std::endl; - return returnvalue::FAILED; -} - ReturnValue_t pst::pstSpiRw(FixedTimeslotTaskIF *thisSequence) { uint32_t length = thisSequence->getPeriodMs(); static_cast(length); diff --git a/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.h b/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.h index 960820b5..1b585d81 100644 --- a/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.h +++ b/linux/fsfwconfig/pollingsequence/pollingSequenceFactory.h @@ -31,9 +31,6 @@ class FixedTimeslotTaskIF; */ namespace pst { -/* 0.4 second period init*/ -ReturnValue_t pstGpio(FixedTimeslotTaskIF* thisSequence); - /** * @brief This function creates the PST for all gomspace devices. * @details diff --git a/mission/devices/SolarArrayDeploymentHandler.cpp b/mission/devices/SolarArrayDeploymentHandler.cpp index c4e9a43b..affc0e2e 100644 --- a/mission/devices/SolarArrayDeploymentHandler.cpp +++ b/mission/devices/SolarArrayDeploymentHandler.cpp @@ -1,171 +1,470 @@ #include "SolarArrayDeploymentHandler.h" -#include -#include -#include -#include +#include -SolarArrayDeploymentHandler::SolarArrayDeploymentHandler( - object_id_t setObjectId_, object_id_t gpioDriverId_, CookieIF* gpioCookie_, - object_id_t mainLineSwitcherObjectId_, pcdu::Switches mainLineSwitch_, gpioId_t deplSA1, - gpioId_t deplSA2, uint32_t burnTimeMs) +#include +#include +#include + +#include "devices/gpioIds.h" +#include "fsfw/ipc/QueueFactory.h" +#include "fsfw/objectmanager/ObjectManager.h" +#include "fsfw_hal/common/gpio/GpioCookie.h" + +static constexpr bool DEBUG_MODE = true; + +SolarArrayDeploymentHandler::SolarArrayDeploymentHandler(object_id_t setObjectId_, + GpioIF& gpioInterface, + PowerSwitchIF& mainLineSwitcher_, + pcdu::Switches mainLineSwitch_, + gpioId_t deplSA1, gpioId_t deplSA2, + SdCardMountedIF& sdcMountedIF) : SystemObject(setObjectId_), - gpioDriverId(gpioDriverId_), - gpioCookie(gpioCookie_), - mainLineSwitcherObjectId(mainLineSwitcherObjectId_), - mainLineSwitch(mainLineSwitch_), + gpioInterface(gpioInterface), deplSA1(deplSA1), deplSA2(deplSA2), - burnTimeMs(burnTimeMs), + mainLineSwitcher(mainLineSwitcher_), + mainLineSwitch(mainLineSwitch_), + sdcMan(sdcMountedIF), actionHelper(this, nullptr) { auto mqArgs = MqArgs(setObjectId_, static_cast(this)); commandQueue = QueueFactory::instance()->createMessageQueue( cmdQueueSize, MessageQueueMessage::MAX_MESSAGE_SIZE, &mqArgs); } -SolarArrayDeploymentHandler::~SolarArrayDeploymentHandler() {} +SolarArrayDeploymentHandler::~SolarArrayDeploymentHandler() = default; ReturnValue_t SolarArrayDeploymentHandler::performOperation(uint8_t operationCode) { - if (operationCode == DeviceHandlerIF::PERFORM_OPERATION) { - handleStateMachine(); - return returnvalue::OK; - } - return returnvalue::OK; -} - -ReturnValue_t SolarArrayDeploymentHandler::initialize() { - ReturnValue_t result = SystemObject::initialize(); - if (result != returnvalue::OK) { - return ObjectManagerIF::CHILD_INIT_FAILED; - } - - gpioInterface = ObjectManager::instance()->get(gpioDriverId); - if (gpioInterface == nullptr) { - sif::error << "SolarArrayDeploymentHandler::initialize: Invalid Gpio interface." << std::endl; - return ObjectManagerIF::CHILD_INIT_FAILED; - } - - result = gpioInterface->addGpios(dynamic_cast(gpioCookie)); - if (result != returnvalue::OK) { - sif::error << "SolarArrayDeploymentHandler::initialize: Failed to initialize Gpio interface" - << std::endl; - return ObjectManagerIF::CHILD_INIT_FAILED; - } - - if (mainLineSwitcherObjectId != objects::NO_OBJECT) { - mainLineSwitcher = ObjectManager::instance()->get(mainLineSwitcherObjectId); - if (mainLineSwitcher == nullptr) { - sif::error - << "SolarArrayDeploymentHandler::initialize: Main line switcher failed to fetch object" - << "from object ID." << std::endl; - return ObjectManagerIF::CHILD_INIT_FAILED; + using namespace std::filesystem; + if (opDivider.checkAndIncrement()) { + auto activeSdc = sdcMan.getActiveSdCard(); + if (activeSdc and activeSdc.value() == sd::SdCard::SLOT_0 and + sdcMan.isSdCardUsable(activeSdc.value())) { + if (exists(SD_0_DEPL_FILE)) { + // perform autonomous deployment handling + performAutonomousDepl(sd::SdCard::SLOT_0, dryRunStringInFile(SD_0_DEPL_FILE)); + } + } else if (activeSdc and activeSdc.value() == sd::SdCard::SLOT_1 and + sdcMan.isSdCardUsable(activeSdc.value())) { + if (exists(SD_1_DEPL_FILE)) { + // perform autonomous deployment handling + performAutonomousDepl(sd::SdCard::SLOT_1, dryRunStringInFile(SD_1_DEPL_FILE)); + } + } else { + // TODO: This is FDIR domain. If both SD cards are not available for whatever reason, + // there is not much we can do except somehow use the scratch buffer which is + // not non-volatile. Implementation effort is considerable as well. } } - - result = actionHelper.initialize(commandQueue); - if (result != returnvalue::OK) { - return ObjectManagerIF::CHILD_INIT_FAILED; - } - + readCommandQueue(); + handleStateMachine(); return returnvalue::OK; } void SolarArrayDeploymentHandler::handleStateMachine() { - switch (stateMachine) { - case WAIT_ON_DELOYMENT_COMMAND: - readCommandQueue(); - break; - case SWITCH_8V_ON: - mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_ON); - mainSwitchCountdown.setTimeout(mainLineSwitcher->getSwitchDelayMs()); - stateMachine = WAIT_ON_8V_SWITCH; - break; - case WAIT_ON_8V_SWITCH: - performWaitOn8VActions(); - break; - case SWITCH_DEPL_GPIOS: - switchDeploymentTransistors(); - break; - case WAIT_ON_DEPLOYMENT_FINISH: - handleDeploymentFinish(); - break; - case WAIT_FOR_MAIN_SWITCH_OFF: - if (mainLineSwitcher->getSwitchState(mainLineSwitch) == PowerSwitchIF::SWITCH_OFF) { - stateMachine = WAIT_ON_DELOYMENT_COMMAND; - } else if (mainSwitchCountdown.hasTimedOut()) { - triggerEvent(MAIN_SWITCH_OFF_TIMEOUT); - sif::error << "SolarArrayDeploymentHandler::handleStateMachine: Failed to switch main" - << " switch off" << std::endl; - stateMachine = WAIT_ON_DELOYMENT_COMMAND; + if (stateMachine == MAIN_POWER_ON) { + mainLineSwitcher.sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_ON); + mainSwitchCountdown.setTimeout(mainLineSwitcher.getSwitchDelayMs()); + stateMachine = WAIT_MAIN_POWER_ON; + sif::info << "S/A Deployment: Deployment power line on" << std::endl; + } + if (stateMachine == MAIN_POWER_OFF) { + // These should never fail + allOff(); + stateMachine = WAIT_MAIN_POWER_OFF; + sif::info << "S/A Deployment: Deployment power line off" << std::endl; + } + if (stateMachine == WAIT_MAIN_POWER_ON) { + if (checkMainPowerOn()) { + if (DEBUG_MODE) { + sif::debug << "SA DEPL FSM: WAIT_MAIN_POWER_ON done -> SWITCH_DEPL_GPIOS" << std::endl; } - break; - default: - sif::debug << "SolarArrayDeploymentHandler::handleStateMachine: Invalid state" << std::endl; - break; + stateMachine = SWITCH_DEPL_GPIOS; + } + } + if (stateMachine == WAIT_MAIN_POWER_OFF) { + if (checkMainPowerOff()) { + if (DEBUG_MODE) { + sif::debug << "SA DEPL FSM: WAIT_MAIN_POWER_OFF done -> FSM DONE" << std::endl; + } + sif::info << "S/A Deployment: FSM done" << std::endl; + finishFsm(returnvalue::OK); + } + } + if (stateMachine == SWITCH_DEPL_GPIOS) { + burnCountdown.setTimeout(fsmInfo.burnCountdownMs); + // This should never fail + channelAlternationCd.resetTimer(); + if (not fsmInfo.dryRun) { + sa2Off(); + sa1On(); + fsmInfo.alternationDummy = true; + } + sif::info << "S/A Deployment: Burning" << std::endl; + triggerEvent(BURN_PHASE_START, fsmInfo.burnCountdownMs, fsmInfo.dryRun); + stateMachine = BURNING; + } + if (stateMachine == BURNING) { + saGpioAlternation(); + if (burnCountdown.hasTimedOut()) { + if (DEBUG_MODE) { + sif::debug << "SA DEPL FSM: BURNING done -> WAIT_MAIN_POWER_OFF" << std::endl; + } + allOff(); + triggerEvent(BURN_PHASE_DONE, fsmInfo.burnCountdownMs, fsmInfo.dryRun); + stateMachine = WAIT_MAIN_POWER_OFF; + } } } -void SolarArrayDeploymentHandler::performWaitOn8VActions() { - if (mainLineSwitcher->getSwitchState(mainLineSwitch) == PowerSwitchIF::SWITCH_ON) { - stateMachine = SWITCH_DEPL_GPIOS; +ReturnValue_t SolarArrayDeploymentHandler::performAutonomousDepl(sd::SdCard sdCard, bool dryRun) { + using namespace std::filesystem; + using namespace std; + auto initFile = [](const char* filename) { + ofstream of(filename); + of << "phase: init\n"; + of << "secs_since_start: 0\n"; + }; + if (sdCard == sd::SdCard::SLOT_0) { + if (not exists(SD_0_DEPLY_INFO)) { + initFile(SD_0_DEPLY_INFO); + } + if (not autonomousDeplForFile(sd::SdCard::SLOT_0, SD_0_DEPLY_INFO, dryRun)) { + initFile(SD_0_DEPLY_INFO); + } + } else if (sdCard == sd::SdCard::SLOT_1) { + if (not exists(SD_1_DEPLY_INFO)) { + initFile(SD_1_DEPLY_INFO); + } + if (not autonomousDeplForFile(sd::SdCard::SLOT_1, SD_1_DEPLY_INFO, dryRun)) { + initFile(SD_1_DEPLY_INFO); + } + } + return returnvalue::OK; +} + +bool SolarArrayDeploymentHandler::autonomousDeplForFile(sd::SdCard sdCard, const char* filename, + bool dryRun) { + using namespace std; + ifstream file(filename); + string line; + string word; + unsigned int lineNum = 0; + AutonomousDeplState deplState; + bool stateSwitch = false; + uint32_t secsSinceBoot = 0; + while (std::getline(file, line)) { + std::istringstream iss(line); + if (lineNum == 0) { + iss >> word; + if (word.find("phase:") == string::npos) { + return false; + } + iss >> word; + if (word.find(PHASE_INIT_STR) != string::npos) { + deplState = AutonomousDeplState::INIT; + } else if (word.find(PHASE_FIRST_BURN_STR) != string::npos) { + deplState = AutonomousDeplState::FIRST_BURN; + } else if (word.find(PHASE_WAIT_STR) != string::npos) { + deplState = AutonomousDeplState::WAIT; + } else if (word.find(PHASE_SECOND_BURN_STR) != string::npos) { + deplState = AutonomousDeplState::SECOND_BURN; + } else if (word.find(PHASE_DONE) != string::npos) { + deplState = AutonomousDeplState::DONE; + } else { + return false; + } + } else if (lineNum == 1) { + iss >> word; + if (iss.bad()) { + return false; + } + if (word.find("secs_since_start:") == string::npos) { + return false; + } + + iss >> secsSinceBoot; + if (iss.bad()) { + return false; + } + if (not initUptime) { + initUptime = secsSinceBoot; + } + + auto switchCheck = [&](AutonomousDeplState expected) { + if (deplState != expected) { + deplState = expected; + stateSwitch = true; + } + }; + if ((secsSinceBoot > FIRST_BURN_START_TIME) and (secsSinceBoot < FIRST_BURN_END_TIME)) { + switchCheck(AutonomousDeplState::FIRST_BURN); + } else if ((secsSinceBoot > WAIT_START_TIME) and (secsSinceBoot < WAIT_END_TIME)) { + switchCheck(AutonomousDeplState::WAIT); + } else if ((secsSinceBoot > SECOND_BURN_START_TIME) and + (secsSinceBoot < SECOND_BURN_END_TIME)) { + switchCheck(AutonomousDeplState::SECOND_BURN); + } else if (secsSinceBoot > SECOND_BURN_END_TIME) { + switchCheck(AutonomousDeplState::DONE); + } + } + lineNum++; + } + if (initUptime) { + secsSinceBoot = initUptime.value(); + } + // Uptime has increased by X seconds so we need to update the uptime count inside the file + secsSinceBoot += Clock::getUptime().tv_sec; + if (stateSwitch or firstAutonomousCycle) { + if (deplState == AutonomousDeplState::FIRST_BURN or + deplState == AutonomousDeplState::SECOND_BURN) { + startFsmOn(config::SA_DEPL_BURN_TIME_SECS, dryRun); + } else if (deplState == AutonomousDeplState::WAIT or deplState == AutonomousDeplState::DONE or + deplState == AutonomousDeplState::INIT) { + startFsmOff(); + } + } + if (deplState == AutonomousDeplState::DONE) { + remove(filename); + if (sdCard == sd::SdCard::SLOT_0) { + remove(SD_0_DEPL_FILE); + } else { + remove(SD_1_DEPL_FILE); + } + triggerEvent(AUTONOMOUS_DEPLOYMENT_COMPLETED); } else { - if (mainSwitchCountdown.hasTimedOut()) { + std::ofstream of(filename); + of << "phase: "; + if (deplState == AutonomousDeplState::INIT) { + of << PHASE_INIT_STR << "\n"; + } else if (deplState == AutonomousDeplState::FIRST_BURN) { + of << PHASE_FIRST_BURN_STR << "\n"; + } else if (deplState == AutonomousDeplState::WAIT) { + of << PHASE_WAIT_STR << "\n"; + } else if (deplState == AutonomousDeplState::SECOND_BURN) { + of << PHASE_SECOND_BURN_STR << "\n"; + } + of << "secs_since_start: " << std::to_string(secsSinceBoot) << "\n"; + } + if (firstAutonomousCycle) { + firstAutonomousCycle = false; + } + return true; +} + +bool SolarArrayDeploymentHandler::checkMainPowerOn() { return checkMainPower(true); } + +bool SolarArrayDeploymentHandler::checkMainPowerOff() { return checkMainPower(false); } + +bool SolarArrayDeploymentHandler::checkMainPower(bool onOff) { + if ((onOff and mainLineSwitcher.getSwitchState(mainLineSwitch) == PowerSwitchIF::SWITCH_ON) or + (not onOff and + mainLineSwitcher.getSwitchState(mainLineSwitch) == PowerSwitchIF::SWITCH_OFF)) { + return true; + } + if (mainSwitchCountdown.hasTimedOut()) { + if (onOff) { triggerEvent(MAIN_SWITCH_ON_TIMEOUT); - actionHelper.finish(false, rememberCommanderId, DEPLOY_SOLAR_ARRAYS, - MAIN_SWITCH_TIMEOUT_FAILURE); - stateMachine = WAIT_ON_DELOYMENT_COMMAND; + } else { + triggerEvent(MAIN_SWITCH_OFF_TIMEOUT); } + if (retryCounter < 3) { + if (onOff) { + stateMachine = MAIN_POWER_ON; + } else { + stateMachine = MAIN_POWER_OFF; + } + retryCounter++; + } else { + finishFsm(MAIN_SWITCH_TIMEOUT_FAILURE); + } + } + return false; +} + +bool SolarArrayDeploymentHandler::startFsmOn(uint32_t burnCountdownSecs, bool dryRun) { + if (stateMachine != StateMachine::IDLE) { + return false; + } + if (burnCountdownSecs > config::SA_DEPL_MAX_BURN_TIME) { + burnCountdownSecs = config::SA_DEPL_MAX_BURN_TIME; + } + fsmInfo.dryRun = dryRun; + fsmInfo.burnCountdownMs = burnCountdownSecs * 1000; + stateMachine = StateMachine::MAIN_POWER_ON; + retryCounter = 0; + return true; +} + +void SolarArrayDeploymentHandler::startFsmOff() { + if (stateMachine != StateMachine::IDLE) { + // off commands override the state machine. Cancel any active action commands. + finishFsm(returnvalue::FAILED); + } + retryCounter = 0; + stateMachine = StateMachine::MAIN_POWER_OFF; +} + +void SolarArrayDeploymentHandler::finishFsm(ReturnValue_t resultForActionHelper) { + retryCounter = 0; + stateMachine = StateMachine::IDLE; + fsmInfo.dryRun = false; + fsmInfo.alternationDummy = false; + if (actionActive) { + bool success = false; + if (resultForActionHelper == returnvalue::OK or + resultForActionHelper == HasActionsIF::EXECUTION_FINISHED) { + success = true; + } + actionHelper.finish(success, rememberCommanderId, activeCmd, resultForActionHelper); } } -void SolarArrayDeploymentHandler::switchDeploymentTransistors() { +void SolarArrayDeploymentHandler::allOff() { + deploymentTransistorsOff(); + mainLineSwitcher.sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF); + mainSwitchCountdown.setTimeout(mainLineSwitcher.getSwitchDelayMs()); +} + +bool SolarArrayDeploymentHandler::dryRunStringInFile(const char* filename) { + std::ifstream ifile(filename); + if (ifile.bad()) { + return false; + } + std::string line; + while (getline(ifile, line)) { + if (line.find("dryrun") != std::string::npos) { + return true; + } + } + return false; +} + +ReturnValue_t SolarArrayDeploymentHandler::executeAction(ActionId_t actionId, + MessageQueueId_t commandedBy, + const uint8_t* data, size_t size) { ReturnValue_t result = returnvalue::OK; - result = gpioInterface->pullHigh(deplSA1); + if (actionId == DEPLOY_SOLAR_ARRAYS_MANUALLY) { + ManualDeploymentCommand cmd; + if (size < cmd.getSerializedSize()) { + return HasActionsIF::INVALID_PARAMETERS; + } + result = cmd.deSerialize(&data, &size, SerializeIF::Endianness::NETWORK); + if (result != returnvalue::OK) { + return result; + } + uint32_t burnCountdown = cmd.getBurnTime(); + if (not startFsmOn(burnCountdown, cmd.isDryRun())) { + return HasActionsIF::IS_BUSY; + } + actionActive = true; + rememberCommanderId = commandedBy; + return result; + } else if (actionId == SWITCH_OFF_DEPLOYMENT) { + startFsmOff(); + actionActive = true; + rememberCommanderId = commandedBy; + return result; + } else { + return HasActionsIF::INVALID_ACTION_ID; + } + return result; +} + +ReturnValue_t SolarArrayDeploymentHandler::saGpioAlternation() { + ReturnValue_t status = returnvalue::OK; + ReturnValue_t result; + if (channelAlternationCd.hasTimedOut() and not fsmInfo.dryRun) { + if (fsmInfo.alternationDummy) { + result = sa1Off(); + if (result != returnvalue::OK) { + status = result; + } + TaskFactory::delayTask(1); + result = sa2On(); + if (result != returnvalue::OK) { + status = result; + } + } else { + result = sa2Off(); + if (result != returnvalue::OK) { + status = result; + } + TaskFactory::delayTask(1); + result = sa1On(); + if (result != returnvalue::OK) { + status = result; + } + } + fsmInfo.alternationDummy = not fsmInfo.alternationDummy; + channelAlternationCd.resetTimer(); + } + return status; +} + +ReturnValue_t SolarArrayDeploymentHandler::deploymentTransistorsOff() { + ReturnValue_t status = returnvalue::OK; + ReturnValue_t result = sa1Off(); if (result != returnvalue::OK) { - sif::debug << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar" - " array deployment switch 1 high " - << std::endl; - /* If gpio switch high failed, state machine is reset to wait for a command reinitiating - * the deployment sequence. */ - stateMachine = WAIT_ON_DELOYMENT_COMMAND; + status = result; + } + result = sa2Off(); + if (result != returnvalue::OK) { + status = result; + } + return status; +} + +ReturnValue_t SolarArrayDeploymentHandler::sa1On() { + ReturnValue_t result = gpioInterface.pullHigh(deplSA1); + if (result != returnvalue::OK) { + sif::warning << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar" + " array deployment switch 1 high" + << std::endl; + // If gpio switch high failed, state machine is reset to wait for a command re-initiating + // the deployment sequence. triggerEvent(DEPL_SA1_GPIO_SWTICH_ON_FAILED); - actionHelper.finish(false, rememberCommanderId, DEPLOY_SOLAR_ARRAYS, SWITCHING_DEPL_SA2_FAILED); - mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF); } - result = gpioInterface->pullHigh(deplSA2); - if (result != returnvalue::OK) { - sif::debug << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar" - " array deployment switch 2 high " - << std::endl; - stateMachine = WAIT_ON_DELOYMENT_COMMAND; - triggerEvent(DEPL_SA2_GPIO_SWTICH_ON_FAILED); - actionHelper.finish(false, rememberCommanderId, DEPLOY_SOLAR_ARRAYS, SWITCHING_DEPL_SA2_FAILED); - mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF); - } - deploymentCountdown.setTimeout(burnTimeMs); - stateMachine = WAIT_ON_DEPLOYMENT_FINISH; + return result; } -void SolarArrayDeploymentHandler::handleDeploymentFinish() { - ReturnValue_t result = returnvalue::OK; - if (deploymentCountdown.hasTimedOut()) { - actionHelper.finish(true, rememberCommanderId, DEPLOY_SOLAR_ARRAYS, returnvalue::OK); - result = gpioInterface->pullLow(deplSA1); - if (result != returnvalue::OK) { - sif::debug << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar" - " array deployment switch 1 low " +ReturnValue_t SolarArrayDeploymentHandler::sa1Off() { + ReturnValue_t result = gpioInterface.pullLow(deplSA1); + if (result != returnvalue::OK) { + sif::warning << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar" + " array deployment switch 1 low" << std::endl; - } - result = gpioInterface->pullLow(deplSA2); - if (result != returnvalue::OK) { - sif::debug << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar" - " array deployment switch 2 low " - << std::endl; - } - mainLineSwitcher->sendSwitchCommand(mainLineSwitch, PowerSwitchIF::SWITCH_OFF); - mainSwitchCountdown.setTimeout(mainLineSwitcher->getSwitchDelayMs()); - stateMachine = WAIT_FOR_MAIN_SWITCH_OFF; + // If gpio switch high failed, state machine is reset to wait for a command re-initiating + // the deployment sequence. + triggerEvent(DEPL_SA1_GPIO_SWTICH_OFF_FAILED); } + return result; +} + +ReturnValue_t SolarArrayDeploymentHandler::sa2On() { + ReturnValue_t result = gpioInterface.pullHigh(deplSA2); + if (result != returnvalue::OK) { + sif::warning << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar" + " array deployment switch 2 high" + << std::endl; + // If gpio switch high failed, state machine is reset to wait for a command re-initiating + // the deployment sequence. + triggerEvent(DEPL_SA2_GPIO_SWTICH_ON_FAILED); + } + return result; +} + +ReturnValue_t SolarArrayDeploymentHandler::sa2Off() { + ReturnValue_t result = gpioInterface.pullLow(deplSA2); + if (result != returnvalue::OK) { + sif::warning << "SolarArrayDeploymentHandler::handleStateMachine: Failed to pull solar" + " array deployment switch 2 low" + << std::endl; + // If gpio switch high failed, state machine is reset to wait for a command re-initiating + // the deployment sequence. + triggerEvent(DEPL_SA2_GPIO_SWTICH_OFF_FAILED); + } + return result; } void SolarArrayDeploymentHandler::readCommandQueue() { @@ -181,27 +480,14 @@ void SolarArrayDeploymentHandler::readCommandQueue() { } } -ReturnValue_t SolarArrayDeploymentHandler::executeAction(ActionId_t actionId, - MessageQueueId_t commandedBy, - const uint8_t* data, size_t size) { - ReturnValue_t result; - if (stateMachine != WAIT_ON_DELOYMENT_COMMAND) { - sif::error << "SolarArrayDeploymentHandler::executeAction: Received command while not in" - << "waiting-on-command-state" << std::endl; - return DEPLOYMENT_ALREADY_EXECUTING; - } - if (actionId != DEPLOY_SOLAR_ARRAYS) { - sif::error << "SolarArrayDeploymentHandler::executeAction: Received invalid command" - << std::endl; - result = COMMAND_NOT_SUPPORTED; - } else { - stateMachine = SWITCH_8V_ON; - rememberCommanderId = commandedBy; - result = returnvalue::OK; - } - return result; -} - MessageQueueId_t SolarArrayDeploymentHandler::getCommandQueue() const { return commandQueue->getId(); } + +ReturnValue_t SolarArrayDeploymentHandler::initialize() { + ReturnValue_t result = actionHelper.initialize(commandQueue); + if (result != returnvalue::OK) { + return ObjectManagerIF::CHILD_INIT_FAILED; + } + return SystemObject::initialize(); +} diff --git a/mission/devices/SolarArrayDeploymentHandler.h b/mission/devices/SolarArrayDeploymentHandler.h index 89be12a6..b935b096 100644 --- a/mission/devices/SolarArrayDeploymentHandler.h +++ b/mission/devices/SolarArrayDeploymentHandler.h @@ -1,22 +1,46 @@ #ifndef MISSION_DEVICES_SOLARARRAYDEPLOYMENT_H_ #define MISSION_DEVICES_SOLARARRAYDEPLOYMENT_H_ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include +#include "devices/powerSwitcherList.h" +#include "eive/definitions.h" #include "events/subsystemIdRanges.h" +#include "fsfw/action/HasActionsIF.h" +#include "fsfw/devicehandlers/CookieIF.h" +#include "fsfw/devicehandlers/DeviceHandlerIF.h" +#include "fsfw/objectmanager/SystemObject.h" +#include "fsfw/power/PowerSwitchIF.h" +#include "fsfw/returnvalues/returnvalue.h" +#include "fsfw/serialize/SerialLinkedListAdapter.h" +#include "fsfw/tasks/ExecutableObjectIF.h" +#include "fsfw/timemanager/Countdown.h" +#include "fsfw_hal/common/gpio/GpioIF.h" +#include "mission/memory/SdCardMountedIF.h" #include "returnvalues/classIds.h" +enum DeploymentChannels : uint8_t { SA_1 = 1, SA_2 = 2 }; + +class ManualDeploymentCommand : public SerialLinkedListAdapter { + public: + ManualDeploymentCommand() { setLinks(); } + + void setLinks() { + setStart(&burnTime); + burnTime.setNext(&dryRun); + } + + uint32_t getBurnTime() const { return burnTime.entry; } + + bool isDryRun() const { return dryRun.entry; } + + private: + SerializeElement burnTime; + SerializeElement dryRun; +}; + /** * @brief This class is used to control the solar array deployment. * @@ -26,8 +50,29 @@ class SolarArrayDeploymentHandler : public ExecutableObjectIF, public SystemObject, public HasActionsIF { public: - static const DeviceCommandId_t DEPLOY_SOLAR_ARRAYS = 0x5; + //! Manual deployment of the solar arrays. Burn time and channels are supplied with TC parameters + static constexpr DeviceCommandId_t DEPLOY_SOLAR_ARRAYS_MANUALLY = 0x05; + static constexpr DeviceCommandId_t SWITCH_OFF_DEPLOYMENT = 0x06; + static constexpr uint32_t FIRST_BURN_START_TIME = config::SA_DEPL_INIT_BUFFER_SECS; + static constexpr uint32_t FIRST_BURN_END_TIME = + FIRST_BURN_START_TIME + config::SA_DEPL_BURN_TIME_SECS; + static constexpr uint32_t WAIT_START_TIME = FIRST_BURN_END_TIME; + static constexpr uint32_t WAIT_END_TIME = WAIT_START_TIME + config::SA_DEPL_WAIT_TIME_SECS; + static constexpr uint32_t SECOND_BURN_START_TIME = WAIT_END_TIME; + static constexpr uint32_t SECOND_BURN_END_TIME = + SECOND_BURN_START_TIME + config::SA_DEPL_WAIT_TIME_SECS; + + static constexpr char SD_0_DEPL_FILE[] = "/mnt/sd0/conf/deployment"; + static constexpr char SD_1_DEPL_FILE[] = "/mnt/sd1/conf/deployment"; + static constexpr char SD_0_DEPLY_INFO[] = "/mnt/sd0/conf/deployment_info.txt"; + static constexpr char SD_1_DEPLY_INFO[] = "/mnt/sd1/conf/deployment_info.txt"; + + static constexpr char PHASE_INIT_STR[] = "init"; + static constexpr char PHASE_FIRST_BURN_STR[] = "first_burn"; + static constexpr char PHASE_WAIT_STR[] = "wait"; + static constexpr char PHASE_SECOND_BURN_STR[] = "second_burn"; + static constexpr char PHASE_DONE[] = "done"; /** * @brief constructor * @@ -43,10 +88,9 @@ class SolarArrayDeploymentHandler : public ExecutableObjectIF, * @param deplSA2 gpioId of the GPIO controlling the deployment 2 transistor. * @param burnTimeMs Time duration the power will be applied to the burn wires. */ - SolarArrayDeploymentHandler(object_id_t setObjectId, object_id_t gpioDriverId, - CookieIF* gpioCookie, object_id_t mainLineSwitcherObjectId, - pcdu::Switches mainLineSwitch, gpioId_t deplSA1, gpioId_t deplSA2, - uint32_t burnTimeMs); + SolarArrayDeploymentHandler(object_id_t setObjectId, GpioIF& gpio, + PowerSwitchIF& mainLineSwitcher, pcdu::Switches mainLineSwitch, + gpioId_t deplSA1, gpioId_t deplSA2, SdCardMountedIF& sdcMountedIF); virtual ~SolarArrayDeploymentHandler(); @@ -58,6 +102,26 @@ class SolarArrayDeploymentHandler : public ExecutableObjectIF, virtual ReturnValue_t initialize() override; private: + enum AutonomousDeplState { INIT, FIRST_BURN, WAIT, SECOND_BURN, DONE }; + + enum StateMachine { + IDLE, + MAIN_POWER_ON, + MAIN_POWER_OFF, + WAIT_MAIN_POWER_ON, + WAIT_MAIN_POWER_OFF, + SWITCH_DEPL_GPIOS, + BURNING + }; + + struct FsmInfo { + // Not required anymore + // DeploymentChannels channel; + bool dryRun; + bool alternationDummy = false; + uint32_t burnCountdownMs = config::SA_DEPL_MAX_BURN_TIME; + }; + static const uint8_t INTERFACE_ID = CLASS_ID::SA_DEPL_HANDLER; static const ReturnValue_t COMMAND_NOT_SUPPORTED = MAKE_RETURN_CODE(0xA0); static const ReturnValue_t DEPLOYMENT_ALREADY_EXECUTING = MAKE_RETURN_CODE(0xA1); @@ -66,23 +130,41 @@ class SolarArrayDeploymentHandler : public ExecutableObjectIF, static const ReturnValue_t SWITCHING_DEPL_SA2_FAILED = MAKE_RETURN_CODE(0xA4); static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SA_DEPL_HANDLER; - static const Event MAIN_SWITCH_ON_TIMEOUT = MAKE_EVENT(0, severity::LOW); - static const Event MAIN_SWITCH_OFF_TIMEOUT = MAKE_EVENT(1, severity::LOW); - static const Event DEPLOYMENT_FAILED = MAKE_EVENT(2, severity::HIGH); - static const Event DEPL_SA1_GPIO_SWTICH_ON_FAILED = MAKE_EVENT(3, severity::HIGH); - static const Event DEPL_SA2_GPIO_SWTICH_ON_FAILED = MAKE_EVENT(4, severity::HIGH); - enum StateMachine { - WAIT_ON_DELOYMENT_COMMAND, - SWITCH_8V_ON, - WAIT_ON_8V_SWITCH, - SWITCH_DEPL_GPIOS, - WAIT_ON_DEPLOYMENT_FINISH, - WAIT_FOR_MAIN_SWITCH_OFF - }; + //! [EXPORT] : [COMMENT] P1: Burn duration in milliseconds, P2: Dry run flag + static constexpr Event BURN_PHASE_START = event::makeEvent(SUBSYSTEM_ID, 0, severity::INFO); + //! [EXPORT] : [COMMENT] P1: Burn duration in milliseconds, P2: Dry run flag + static constexpr Event BURN_PHASE_DONE = event::makeEvent(SUBSYSTEM_ID, 1, severity::INFO); + static constexpr Event MAIN_SWITCH_ON_TIMEOUT = event::makeEvent(SUBSYSTEM_ID, 2, severity::LOW); + static constexpr Event MAIN_SWITCH_OFF_TIMEOUT = event::makeEvent(SUBSYSTEM_ID, 3, severity::LOW); + static constexpr Event DEPL_SA1_GPIO_SWTICH_ON_FAILED = + event::makeEvent(SUBSYSTEM_ID, 4, severity::HIGH); + static constexpr Event DEPL_SA2_GPIO_SWTICH_ON_FAILED = + event::makeEvent(SUBSYSTEM_ID, 5, severity::HIGH); + static constexpr Event DEPL_SA1_GPIO_SWTICH_OFF_FAILED = + event::makeEvent(SUBSYSTEM_ID, 6, severity::HIGH); + static constexpr Event DEPL_SA2_GPIO_SWTICH_OFF_FAILED = + event::makeEvent(SUBSYSTEM_ID, 7, severity::HIGH); + static constexpr Event AUTONOMOUS_DEPLOYMENT_COMPLETED = + event::makeEvent(SUBSYSTEM_ID, 8, severity::INFO); - StateMachine stateMachine = WAIT_ON_DELOYMENT_COMMAND; + FsmInfo fsmInfo; + StateMachine stateMachine = IDLE; + bool actionActive = false; + bool firstAutonomousCycle = true; + ActionId_t activeCmd = HasActionsIF::INVALID_ACTION_ID; + std::optional initUptime; + PeriodicOperationDivider opDivider = PeriodicOperationDivider(5); + uint8_t retryCounter = 3; + bool startFsmOn(uint32_t burnCountdownSecs, bool dryRun); + void startFsmOff(); + + void finishFsm(ReturnValue_t resultForActionHelper); + + ReturnValue_t performAutonomousDepl(sd::SdCard sdCard, bool dryRun); + bool dryRunStringInFile(const char* filename); + bool autonomousDeplForFile(sd::SdCard sdCard, const char* filename, bool dryRun); /** * This countdown is used to check if the PCDU sets the 8V line on in the intended time. */ @@ -91,7 +173,10 @@ class SolarArrayDeploymentHandler : public ExecutableObjectIF, /** * This countdown is used to wait for the burn wire being successful cut. */ - Countdown deploymentCountdown; + Countdown burnCountdown; + + Countdown channelAlternationCd = + Countdown(config::SA_DEPL_CHANNEL_ALTERNATION_INTERVAL_SECS * 1000); /** * The message queue id of the component commanding an action will be stored in this variable. @@ -101,36 +186,25 @@ class SolarArrayDeploymentHandler : public ExecutableObjectIF, /** Size of command queue */ size_t cmdQueueSize = 20; - - /** The object ID of the GPIO driver which switches the deployment transistors */ - object_id_t gpioDriverId; - - CookieIF* gpioCookie; - - /** Object id of the object responsible to switch the 8V power input. Typically the PCDU. */ - object_id_t mainLineSwitcherObjectId; - - /** Switch number of the 8V power switch */ - uint8_t mainLineSwitch; - + GpioIF& gpioInterface; gpioId_t deplSA1; gpioId_t deplSA2; - GpioIF* gpioInterface = nullptr; - - /** Time duration switches are active to cut the burn wire */ - uint32_t burnTimeMs; - - /** Queue to receive messages from other objects. */ - MessageQueueIF* commandQueue = nullptr; - /** * After initialization this pointer will hold the reference to the main line switcher object. */ - PowerSwitchIF* mainLineSwitcher = nullptr; + PowerSwitchIF& mainLineSwitcher; + + /** Switch number of the 8V power switch */ + uint8_t mainLineSwitch; + + SdCardMountedIF& sdcMan; ActionHelper actionHelper; + /** Queue to receive messages from other objects. */ + MessageQueueIF* commandQueue = nullptr; + void readCommandQueue(); /** @@ -142,18 +216,18 @@ class SolarArrayDeploymentHandler : public ExecutableObjectIF, * @brief This function polls the 8V switch state and changes the state machine when the * switch has been enabled. */ - void performWaitOn8VActions(); + bool checkMainPowerOn(); + bool checkMainPowerOff(); + bool checkMainPower(bool onOff); - /** - * @brief This functions handles the switching of the solar array deployment transistors. - */ - void switchDeploymentTransistors(); + void allOff(); - /** - * @brief This function performs actions to finish the deployment. Essentially switches - * are turned of after the burn time has expired. - */ - void handleDeploymentFinish(); + ReturnValue_t deploymentTransistorsOff(); + ReturnValue_t saGpioAlternation(); + ReturnValue_t sa1On(); + ReturnValue_t sa1Off(); + ReturnValue_t sa2On(); + ReturnValue_t sa2Off(); }; #endif /* MISSION_DEVICES_SOLARARRAYDEPLOYMENT_H_ */ diff --git a/tmtc b/tmtc index 4c3f5f28..646cf1a1 160000 --- a/tmtc +++ b/tmtc @@ -1 +1 @@ -Subproject commit 4c3f5f28256be0dbfc5b46ea87f8f484c93a9996 +Subproject commit 646cf1a14e0fa61d5e91e26a46017be557bad642