diff --git a/CHANGELOG.md b/CHANGELOG.md index 5620ec22..be62180f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,12 @@ will consitute of a breaking change warranting a new major release: to a regression, so commanding the SDC state machine externally lead to confusing results. - Fixed a bug in persistent TM store, where the active file was not reset of SD card switches. SD card switch from 0 to 1 and vice-versa works without errors from persistent TM stores now. +- Add a way for the SUS polling to detect broken or off devices by checking the retrieved + temperature for the all-ones value (0x0fff). +- Better reply result handling for the ACS board devices. +- ADIS1650X initial timeout handling now performed in device handler. +- The RW assembly and TCS board assembly now perform proper power switch handling for their + recovery handling. ## Changed @@ -29,6 +35,8 @@ will consitute of a breaking change warranting a new major release: the active SD card is switched or there is a transition from hot redundant to cold redundant mode. This gives other tasks some time to register the SD cards being unusable, and therefore provides a way for them to perform any re-initialization tasks necessary after SD card switches. +- The TCS controller pauses operations related to the TCS board assembly (reading sensors and + the primary control loop) while a TCS board recovery is on-going. ## Changed diff --git a/dummies/helperFactory.cpp b/dummies/helperFactory.cpp index 404b640c..f2323bee 100644 --- a/dummies/helperFactory.cpp +++ b/dummies/helperFactory.cpp @@ -40,6 +40,7 @@ #include "mission/system/com/comModeTree.h" #include "mission/system/tree/payloadModeTree.h" #include "mission/system/tree/tcsModeTree.h" +#include "mission/tcs/defs.h" void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpioIF) { new ComIFDummy(objects::DUMMY_COM_IF); @@ -202,7 +203,8 @@ void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpio new TemperatureSensorInserter(objects::THERMAL_TEMP_INSERTER, rtdSensorDummies, tmpSensorDummies); - TcsBoardAssembly* tcsBoardAssy = ObjectFactory::createTcsBoardAssy(pwrSwitcher); + TcsBoardAssembly* tcsBoardAssy = + ObjectFactory::createTcsBoardAssy(pwrSwitcher, tcs::TCS_BOARD_SHORTLY_UNAVAILABLE); for (auto& rtd : rtdSensorDummies) { rtd.second->connectModeTreeParent(*tcsBoardAssy); } diff --git a/linux/ObjectFactory.cpp b/linux/ObjectFactory.cpp index 66a3195b..72f30278 100644 --- a/linux/ObjectFactory.cpp +++ b/linux/ObjectFactory.cpp @@ -29,6 +29,7 @@ #include "mission/system/acs/acsModeTree.h" #include "mission/system/tree/payloadModeTree.h" #include "mission/system/tree/tcsModeTree.h" +#include "mission/tcs/defs.h" void ObjectFactory::createSunSensorComponents(GpioIF* gpioComIF, SpiComIF* spiComIF, PowerSwitchIF& pwrSwitcher, std::string spiDev, @@ -278,7 +279,8 @@ void ObjectFactory::createRtdComponents(std::string spiDev, GpioIF* gpioComIF, std::array rtds = {}; RtdFdir* rtdFdir = nullptr; - TcsBoardAssembly* tcsBoardAss = ObjectFactory::createTcsBoardAssy(*pwrSwitcher); + TcsBoardAssembly* tcsBoardAss = + ObjectFactory::createTcsBoardAssy(*pwrSwitcher, tcs::TCS_BOARD_SHORTLY_UNAVAILABLE); // Create special low level reader communication interface new Max31865RtdPolling(objects::SPI_RTD_COM_IF, comIF, gpioComIF); diff --git a/linux/acs/AcsBoardPolling.cpp b/linux/acs/AcsBoardPolling.cpp index b289ef70..bc3dff74 100644 --- a/linux/acs/AcsBoardPolling.cpp +++ b/linux/acs/AcsBoardPolling.cpp @@ -113,7 +113,8 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send if (req->mode != adis.mode) { if (req->mode == acs::SimpleSensorMode::NORMAL) { adis.type = req->type; - adis.countdown.setTimeout(adis1650x::START_UP_TIME); + // The initial countdown is handled by the device handler now. + // adis.countdown.setTimeout(adis1650x::START_UP_TIME); if (adis.type == adis1650x::Type::ADIS16507) { adis.ownReply.data.accelScaling = adis1650x::ACCELEROMETER_RANGE_16507; } else if (adis.type == adis1650x::Type::ADIS16505) { @@ -127,6 +128,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send adis.ownReply.cfgWasSet = false; adis.ownReply.dataWasSet = false; } + adis.replyResult = returnvalue::FAILED; adis.mode = req->mode; } return returnvalue::OK; @@ -145,6 +147,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send } else { gyro.ownReply.cfgWasSet = false; } + gyro.replyResult = returnvalue::FAILED; gyro.mode = req->mode; } return returnvalue::OK; @@ -163,6 +166,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send mgm.ownReply.dataWasSet = false; mgm.ownReply.temperatureWasSet = false; } + mgm.replyResult = returnvalue::FAILED; mgm.mode = req->mode; } return returnvalue::OK; @@ -180,6 +184,7 @@ ReturnValue_t AcsBoardPolling::sendMessage(CookieIF* cookie, const uint8_t* send } else { mgm.ownReply.dataWasRead = false; } + mgm.replyResult = returnvalue::FAILED; mgm.mode = req->mode; } return returnvalue::OK; @@ -309,18 +314,18 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) { std::memcpy(cmdBuf.data() + 1, l3g.sensorCfg, 5); result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6); if (result != returnvalue::OK) { - l3g.replyResult = returnvalue::OK; + l3g.replyResult = result; } // Ignore useless reply and red config cmdBuf[0] = l3gd20h::CTRL_REG_1 | l3gd20h::AUTO_INCREMENT_MASK | l3gd20h::READ_MASK; std::memset(cmdBuf.data() + 1, 0, 5); result = spiComIF.sendMessage(l3g.cookie, cmdBuf.data(), 6); if (result != returnvalue::OK) { - l3g.replyResult = returnvalue::OK; + l3g.replyResult = result; } result = spiComIF.readReceivedMessage(l3g.cookie, &rawReply, &dummy); if (result != returnvalue::OK) { - l3g.replyResult = returnvalue::OK; + l3g.replyResult = result; } MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); // Cross check configuration as verification that communication is working @@ -331,6 +336,7 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) { return; } } + l3g.replyResult = returnvalue::OK; l3g.performStartup = false; l3g.ownReply.cfgWasSet = true; l3g.ownReply.sensitivity = l3gd20h::ctrlReg4ToSensitivity(l3g.sensorCfg[3]); @@ -357,6 +363,7 @@ void AcsBoardPolling::gyroL3gHandler(GyroL3g& l3g) { return; } } + l3g.replyResult = returnvalue::OK; l3g.ownReply.statusReg = rawReply[l3gd20h::STATUS_IDX]; l3g.ownReply.angVelocities[0] = (rawReply[l3gd20h::OUT_X_H] << 8) | rawReply[l3gd20h::OUT_X_L]; l3g.ownReply.angVelocities[1] = (rawReply[l3gd20h::OUT_Y_H] << 8) | rawReply[l3gd20h::OUT_Y_L]; @@ -443,20 +450,15 @@ ReturnValue_t AcsBoardPolling::readAdisCfg(SpiCookie& cookie, size_t transferLen void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) { ReturnValue_t result; acs::SimpleSensorMode mode = acs::SimpleSensorMode::OFF; - bool cdHasTimedOut = false; bool mustPerformStartup = false; { MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); mode = gyro.mode; - cdHasTimedOut = gyro.countdown.hasTimedOut(); mustPerformStartup = gyro.performStartup; } if (mode == acs::SimpleSensorMode::OFF) { return; } - if (not cdHasTimedOut) { - return; - } if (mustPerformStartup) { uint8_t regList[6]; // Read configuration @@ -495,6 +497,7 @@ void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) { gyro.ownReply.cfg.prodId = prodId; gyro.ownReply.data.sensitivity = adis1650x::rangMdlToSensitivity(gyro.ownReply.cfg.rangMdl); gyro.performStartup = false; + gyro.replyResult = returnvalue::OK; } // Read regular registers std::memcpy(cmdBuf.data(), adis1650x::BURST_READ_ENABLE.data(), @@ -533,6 +536,7 @@ void AcsBoardPolling::gyroAdisHandler(GyroAdis& gyro) { } MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); + gyro.replyResult = returnvalue::OK; gyro.ownReply.dataWasSet = true; gyro.ownReply.cfg.diagStat = rawReply[2] << 8 | rawReply[3]; gyro.ownReply.data.angVelocities[0] = (rawReply[4] << 8) | rawReply[5]; @@ -590,6 +594,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) { } // Done here. We can always read back config and data during periodic handling mgm.performStartup = false; + mgm.replyResult = returnvalue::OK; } cmdBuf[0] = mgmLis3::readCommand(mgmLis3::CTRL_REG1, true); std::memset(cmdBuf.data() + 1, 0, mgmLis3::NR_OF_DATA_AND_CFG_REGISTERS); @@ -607,7 +612,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) { // Verify communication by re-checking config if (rawReply[1] != mgm.cfg[0] or rawReply[2] != mgm.cfg[1] or rawReply[3] != mgm.cfg[2] or rawReply[4] != mgm.cfg[3] or rawReply[5] != mgm.cfg[4]) { - mgm.replyResult = result; + mgm.replyResult = returnvalue::FAILED; return; } { @@ -634,6 +639,7 @@ void AcsBoardPolling::mgmLis3Handler(MgmLis3& mgm) { return; } MutexGuard mg(ipcLock, LOCK_TYPE, LOCK_TIMEOUT, LOCK_CTX); + mgm.replyResult = returnvalue::OK; mgm.ownReply.temperatureWasSet = true; mgm.ownReply.temperatureRaw = (rawReply[2] << 8) | rawReply[1]; } @@ -704,6 +710,7 @@ void AcsBoardPolling::mgmRm3100Handler(MgmRm3100& mgm) { return; } mgm.performStartup = false; + mgm.replyResult = returnvalue::OK; } // Regular read operation cmdBuf[0] = mgmRm3100::MEASUREMENT_REG_START | mgmRm3100::READ_MASK; @@ -725,6 +732,7 @@ void AcsBoardPolling::mgmRm3100Handler(MgmRm3100& mgm) { mgm.ownReply.scaleFactors[idx] = 1.0 / mgmRm3100::DEFAULT_GAIN; } mgm.ownReply.dataWasRead = true; + mgm.replyResult = returnvalue::OK; // Bitshift trickery to account for 24 bit signed value. mgm.ownReply.mgmValuesRaw[0] = ((rawReply[1] << 24) | (rawReply[2] << 16) | (rawReply[3] << 8)) >> 8; diff --git a/linux/acs/SusPolling.cpp b/linux/acs/SusPolling.cpp index aea92ffe..1da5ef5e 100644 --- a/linux/acs/SusPolling.cpp +++ b/linux/acs/SusPolling.cpp @@ -73,6 +73,7 @@ ReturnValue_t SusPolling::sendMessage(CookieIF* cookie, const uint8_t* sendData, susDevs[susIdx].ownReply.cfgWasSet = false; susDevs[susIdx].ownReply.dataWasSet = false; } + susDevs[susIdx].replyResult = returnvalue::FAILED; susDevs[susIdx].mode = susReq->mode; } if (state == InternalState::IDLE) { @@ -95,11 +96,14 @@ ReturnValue_t SusPolling::readReceivedMessage(CookieIF* cookie, uint8_t** buffer if (susIdx < 0) { return FAILED; } + if (susDevs[susIdx].replyResult != returnvalue::OK) { + return susDevs[susIdx].replyResult; + } MutexGuard mg(ipcLock); std::memcpy(&susDevs[susIdx].readerReply, &susDevs[susIdx].ownReply, sizeof(acs::SusReply)); *buffer = reinterpret_cast(&susDevs[susIdx].readerReply); *size = sizeof(acs::SusReply); - return OK; + return susDevs[susIdx].replyResult; } ReturnValue_t SusPolling::handleSusPolling() { @@ -164,11 +168,18 @@ ReturnValue_t SusPolling::handleSusPolling() { } MutexGuard mg(ipcLock); susDevs[idx].ownReply.tempRaw = ((rawReply[0] & 0x0f) << 8) | rawReply[1]; - for (unsigned chIdx = 0; chIdx < 6; chIdx++) { - susDevs[idx].ownReply.channelsRaw[chIdx] = - (rawReply[chIdx * 2 + 2] << 8) | rawReply[chIdx * 2 + 3]; + // Reply is all ones. Sensor is probably off or faulty when + // it should not be. + if (susDevs[idx].ownReply.tempRaw == 0x0fff) { + susDevs[idx].replyResult = returnvalue::FAILED; + } else { + susDevs[idx].replyResult = returnvalue::OK; + for (unsigned chIdx = 0; chIdx < 6; chIdx++) { + susDevs[idx].ownReply.channelsRaw[chIdx] = + (rawReply[chIdx * 2 + 2] << 8) | rawReply[chIdx * 2 + 3]; + } + susDevs[idx].ownReply.dataWasSet = true; } - susDevs[idx].ownReply.dataWasSet = true; } } return OK; diff --git a/mission/acs/GyrAdis1650XHandler.cpp b/mission/acs/GyrAdis1650XHandler.cpp index 57033e80..b9e0b049 100644 --- a/mission/acs/GyrAdis1650XHandler.cpp +++ b/mission/acs/GyrAdis1650XHandler.cpp @@ -56,6 +56,9 @@ ReturnValue_t GyrAdis1650XHandler::buildNormalDeviceCommand(DeviceCommandId_t *i ReturnValue_t GyrAdis1650XHandler::buildTransitionDeviceCommand(DeviceCommandId_t *id) { switch (internalState) { case (InternalState::STARTUP): { + if (breakCountdown.isBusy()) { + return NOTHING_TO_SEND; + } *id = adis1650x::REQUEST; return preparePeriodicRequest(acs::SimpleSensorMode::NORMAL); } diff --git a/mission/controller/ThermalController.cpp b/mission/controller/ThermalController.cpp index 96b6de86..fc2da4ef 100644 --- a/mission/controller/ThermalController.cpp +++ b/mission/controller/ThermalController.cpp @@ -21,7 +21,8 @@ #define LOWER_EBAND_UPPER_LIMITS 0 #define LOWER_PLOC_UPPER_LIMITS 0 -ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater) +ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater, + const std::atomic_bool& tcsBoardShortUnavailable) : ExtendedControllerBase(objectId), heaterHandler(heater), sensorTemperatures(this), @@ -66,7 +67,8 @@ ThermalController::ThermalController(object_id_t objectId, HeaterHandler& heater susSet8(objects::SUS_8_R_LOC_XBYBZB_PT_YB), susSet9(objects::SUS_9_R_LOC_XBYBZB_PT_YF), susSet10(objects::SUS_10_N_LOC_XMYBZF_PT_ZF), - susSet11(objects::SUS_11_R_LOC_XBYMZB_PT_ZB) { + susSet11(objects::SUS_11_R_LOC_XBYMZB_PT_ZB), + tcsBrdShortlyUnavailable(tcsBoardShortUnavailable) { resetSensorsArray(); } @@ -134,10 +136,12 @@ void ThermalController::performControlOperation() { } } - { - PoolReadGuard pg(&sensorTemperatures); - if (pg.getReadResult() == returnvalue::OK) { - copySensors(); + if (not tcsBrdShortlyUnavailable) { + { + PoolReadGuard pg(&sensorTemperatures); + if (pg.getReadResult() == returnvalue::OK) { + copySensors(); + } } } { @@ -182,7 +186,7 @@ void ThermalController::performControlOperation() { } setMode(MODE_OFF); } - } else if (mode != MODE_OFF) { + } else if (mode != MODE_OFF and not tcsBrdShortlyUnavailable) { performThermalModuleCtrl(heaterSwitchStateArray); } cycles++; diff --git a/mission/controller/ThermalController.h b/mission/controller/ThermalController.h index 631a37aa..54b05216 100644 --- a/mission/controller/ThermalController.h +++ b/mission/controller/ThermalController.h @@ -22,6 +22,7 @@ #include #include +#include #include /** @@ -94,7 +95,8 @@ class ThermalController : public ExtendedControllerBase { static constexpr int16_t SANITY_LIMIT_LOWER_TEMP = -80; static constexpr int16_t SANITY_LIMIT_UPPER_TEMP = 160; - ThermalController(object_id_t objectId, HeaterHandler& heater); + ThermalController(object_id_t objectId, HeaterHandler& heater, + const std::atomic_bool& tcsBoardShortUnavailable); ReturnValue_t initialize() override; @@ -181,6 +183,10 @@ class ThermalController : public ExtendedControllerBase { susMax1227::SusDataset susSet10; susMax1227::SusDataset susSet11; + // If the TCS board in unavailable, for example due to a recovery, skip + // some TCS controller tasks to avoid unnecessary events. + const std::atomic_bool& tcsBrdShortlyUnavailable = false; + lp_var_t tempQ7s = lp_var_t(objects::CORE_CONTROLLER, core::PoolIds::TEMPERATURE); lp_var_t battTemp1 = lp_var_t(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_1); lp_var_t battTemp2 = lp_var_t(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_2); diff --git a/mission/genericFactory.cpp b/mission/genericFactory.cpp index 6ce0b7c0..cbef2f77 100644 --- a/mission/genericFactory.cpp +++ b/mission/genericFactory.cpp @@ -48,6 +48,7 @@ #include "mission/system/acs/RwAssembly.h" #include "mission/system/acs/acsModeTree.h" #include "mission/system/tree/tcsModeTree.h" +#include "mission/tcs/defs.h" #include "mission/tmtc/tmFilters.h" #include "objects/systemObjectList.h" #include "tmtc/pusIds.h" @@ -90,6 +91,8 @@ EiveFaultHandler EIVE_FAULT_HANDLER; } // namespace cfdp +std::atomic_bool tcs::TCS_BOARD_SHORTLY_UNAVAILABLE = false; + void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFunnel** pusFunnel, CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan, StorageManagerIF** ipcStore, StorageManagerIF** tmStore, @@ -300,7 +303,8 @@ void ObjectFactory::createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF& } void ObjectFactory::createThermalController(HeaterHandler& heaterHandler) { - auto* tcsCtrl = new ThermalController(objects::THERMAL_CONTROLLER, heaterHandler); + auto* tcsCtrl = new ThermalController(objects::THERMAL_CONTROLLER, heaterHandler, + tcs::TCS_BOARD_SHORTLY_UNAVAILABLE); tcsCtrl->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); } void ObjectFactory::createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch, @@ -366,10 +370,12 @@ void ObjectFactory::createAcsBoardAssy(PowerSwitchIF& pwrSwitcher, acsAss->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM); } -TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher) { +TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher, + std::atomic_bool& tcsShortlyUnavailable) { TcsBoardHelper helper(RTD_INFOS); - auto* tcsBoardAss = new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher, - power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper); + auto* tcsBoardAss = + new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher, + power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper, tcsShortlyUnavailable); tcsBoardAss->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); return tcsBoardAss; } diff --git a/mission/genericFactory.h b/mission/genericFactory.h index 496c516b..6cd2068d 100644 --- a/mission/genericFactory.h +++ b/mission/genericFactory.h @@ -55,7 +55,8 @@ void createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch, void createSusAssy(PowerSwitchIF& pwrSwitcher, std::array suses); void createAcsBoardAssy(PowerSwitchIF& pwrSwitcher, std::array assemblyDhbs, ExtendedControllerBase* gpsCtrl, GpioIF* gpioComIF); -TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher); +TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher, + std::atomic_bool& tcsShortlyUnavailable); } // namespace ObjectFactory diff --git a/mission/power/defs.h b/mission/power/defs.h index b10fe1ff..13e5e63f 100644 --- a/mission/power/defs.h +++ b/mission/power/defs.h @@ -50,6 +50,7 @@ static constexpr Event FDIR_REACTION_IGNORED = event::makeEvent(SUBSYSTEM_ID, 3, enum class States { IDLE, SWITCHING_POWER, CHECKING_POWER, MODE_COMMANDING }; enum class OpCodes { NONE, TO_OFF_DONE, TO_NOT_OFF_DONE, TIMEOUT_OCCURED }; +enum RecoveryCustomStates { IDLE, POWER_SWITCHING_OFF, POWER_SWITCHING_ON, DONE }; } // namespace power namespace duallane { diff --git a/mission/system/CMakeLists.txt b/mission/system/CMakeLists.txt index 51e47ef4..e5473de8 100644 --- a/mission/system/CMakeLists.txt +++ b/mission/system/CMakeLists.txt @@ -6,5 +6,6 @@ add_subdirectory(fdir) add_subdirectory(power) target_sources( - ${LIB_EIVE_MISSION} PRIVATE systemTree.cpp DualLanePowerStateMachine.cpp - EiveSystem.cpp treeUtil.cpp) + ${LIB_EIVE_MISSION} + PRIVATE systemTree.cpp DualLanePowerStateMachine.cpp EiveSystem.cpp + treeUtil.cpp SharedPowerAssemblyBase.cpp) diff --git a/mission/system/SharedPowerAssemblyBase.cpp b/mission/system/SharedPowerAssemblyBase.cpp new file mode 100644 index 00000000..5c325b51 --- /dev/null +++ b/mission/system/SharedPowerAssemblyBase.cpp @@ -0,0 +1,91 @@ +#include "SharedPowerAssemblyBase.h" + +SharedPowerAssemblyBase::SharedPowerAssemblyBase(object_id_t objectId, PowerSwitchIF* pwrSwitcher, + power::Switch_t switchId, uint16_t cmdQueueDepth) + : AssemblyBase(objectId, cmdQueueDepth), switcher(pwrSwitcher, switchId) {} + +void SharedPowerAssemblyBase::performChildOperation() { + auto state = switcher.getState(); + if (state != PowerSwitcher::WAIT_OFF and state != PowerSwitcher::WAIT_ON) { + AssemblyBase::performChildOperation(); + return; + } + switcher.doStateMachine(); + if (state == PowerSwitcher::WAIT_OFF and switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) { + // Indicator that a transition to off is finished + AssemblyBase::handleModeReached(); + } else if (state == PowerSwitcher::WAIT_ON and + switcher.getState() == PowerSwitcher::SWITCH_IS_ON) { + // Indicator that mode commanding can be performed now + AssemblyBase::startTransition(targetMode, targetSubmode); + } +} + +void SharedPowerAssemblyBase::startTransition(Mode_t mode, Submode_t submode) { + if (mode != MODE_OFF) { + switcher.turnOn(true); + switcher.doStateMachine(); + if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) { + AssemblyBase::startTransition(mode, submode); + } else { + // Need to wait with mode commanding until power switcher is done + targetMode = mode; + targetSubmode = submode; + } + } else { + // Perform regular mode commanding first + AssemblyBase::startTransition(mode, submode); + } +} + +void SharedPowerAssemblyBase::handleModeReached() { + if (targetMode == MODE_OFF) { + switcher.turnOff(true); + switcher.doStateMachine(); + // Need to wait with call to AssemblyBase::handleModeReached until power switcher is done + if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) { + AssemblyBase::handleModeReached(); + } + } else { + AssemblyBase::handleModeReached(); + } +} + +bool SharedPowerAssemblyBase::checkAndHandleRecovery() { + using namespace power; + if (recoveryState == RECOVERY_IDLE) { + return AssemblyBase::checkAndHandleRecovery(); + } + if (customRecoveryStates == IDLE) { + switcher.turnOff(); + customRecoveryStates = RecoveryCustomStates::POWER_SWITCHING_OFF; + } + if (customRecoveryStates == POWER_SWITCHING_OFF) { + switcher.doStateMachine(); + if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) { + customRecoveryStates = RecoveryCustomStates::POWER_SWITCHING_ON; + switcher.turnOn(); + } + } + if (customRecoveryStates == POWER_SWITCHING_ON) { + switcher.doStateMachine(); + if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) { + customRecoveryStates = RecoveryCustomStates::DONE; + } + } + if (customRecoveryStates == DONE) { + bool pendingRecovery = AssemblyBase::checkAndHandleRecovery(); + if (not pendingRecovery) { + customRecoveryStates = RecoveryCustomStates::IDLE; + } + // For a recovery on one side, only do the recovery once + for (auto& child : childrenMap) { + if (healthHelper.healthTable->getHealth(child.first) == HasHealthIF::NEEDS_RECOVERY) { + sendHealthCommand(child.second.commandQueue, HEALTHY); + child.second.healthChanged = false; + } + } + return pendingRecovery; + } + return true; +} diff --git a/mission/system/SharedPowerAssemblyBase.h b/mission/system/SharedPowerAssemblyBase.h new file mode 100644 index 00000000..c269a52f --- /dev/null +++ b/mission/system/SharedPowerAssemblyBase.h @@ -0,0 +1,27 @@ +#ifndef MISSION_SYSTEM_SHAREDPOWERASSEMBLYBASE_H_ +#define MISSION_SYSTEM_SHAREDPOWERASSEMBLYBASE_H_ + +#include +#include +#include + +/** + * Base class which contains common functions for assemblies where the power line is shared + * among the devices in the assembly. + */ +class SharedPowerAssemblyBase : public AssemblyBase { + public: + SharedPowerAssemblyBase(object_id_t objectId, PowerSwitchIF* pwrSwitcher, + power::Switch_t switchId, uint16_t cmdQueueDepth = 8); + + protected: + PowerSwitcher switcher; + power::RecoveryCustomStates customRecoveryStates = power::RecoveryCustomStates::IDLE; + + void performChildOperation() override; + void startTransition(Mode_t mode, Submode_t submode) override; + void handleModeReached() override; + virtual bool checkAndHandleRecovery() override; +}; + +#endif /* MISSION_SYSTEM_SHAREDPOWERASSEMBLYBASE_H_ */ diff --git a/mission/system/acs/DualLaneAssemblyBase.h b/mission/system/acs/DualLaneAssemblyBase.h index 40d9aefc..a30bad5b 100644 --- a/mission/system/acs/DualLaneAssemblyBase.h +++ b/mission/system/acs/DualLaneAssemblyBase.h @@ -39,12 +39,7 @@ class DualLaneAssemblyBase : public AssemblyBase, public ConfirmsFailuresIF { SideSwitchState sideSwitchState = SideSwitchState::NONE; duallane::Submodes targetSubmodeForSideSwitch = duallane::Submodes::B_SIDE; - enum RecoveryCustomStates { - IDLE, - POWER_SWITCHING_OFF, - POWER_SWITCHING_ON, - DONE - } customRecoveryStates = RecoveryCustomStates::IDLE; + power::RecoveryCustomStates customRecoveryStates = power::RecoveryCustomStates::IDLE; MessageQueueIF* eventQueue = nullptr; diff --git a/mission/system/acs/RwAssembly.cpp b/mission/system/acs/RwAssembly.cpp index eacd8d0d..68b7c2da 100644 --- a/mission/system/acs/RwAssembly.cpp +++ b/mission/system/acs/RwAssembly.cpp @@ -1,8 +1,8 @@ #include "RwAssembly.h" -RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher, +RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switchId, RwHelper helper) - : AssemblyBase(objectId), helper(helper), switcher(pwrSwitcher, switcher) { + : SharedPowerAssemblyBase(objectId, pwrSwitcher, switchId), helper(helper) { ModeListEntry entry; for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) { entry.setObject(helper.rwIds[idx]); @@ -12,26 +12,8 @@ RwAssembly::RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power:: } } -void RwAssembly::performChildOperation() { - auto state = switcher.getState(); - if (state != PowerSwitcher::WAIT_OFF and state != PowerSwitcher::WAIT_ON) { - AssemblyBase::performChildOperation(); - return; - } - switcher.doStateMachine(); - if (state == PowerSwitcher::WAIT_OFF and switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) { - // Indicator that a transition to off is finished - AssemblyBase::handleModeReached(); - } else if (state == PowerSwitcher::WAIT_ON and - switcher.getState() == PowerSwitcher::SWITCH_IS_ON) { - // Indicator that mode commanding can be performed now - AssemblyBase::startTransition(targetMode, targetSubmode); - } -} - ReturnValue_t RwAssembly::commandChildren(Mode_t mode, Submode_t submode) { ReturnValue_t result = returnvalue::OK; - modeTransitionFailedSwitch = true; // Initialize the mode table to ensure all devices are in a defined state for (uint8_t idx = 0; idx < NUMBER_RWS; idx++) { modeTable[idx].setMode(MODE_OFF); @@ -76,36 +58,6 @@ ReturnValue_t RwAssembly::isModeCombinationValid(Mode_t mode, Submode_t submode) return HasModesIF::INVALID_MODE; } -void RwAssembly::startTransition(Mode_t mode, Submode_t submode) { - if (mode != MODE_OFF) { - switcher.turnOn(true); - switcher.doStateMachine(); - if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) { - AssemblyBase::startTransition(mode, submode); - } else { - // Need to wait with mode commanding until power switcher is done - targetMode = mode; - targetSubmode = submode; - } - } else { - // Perform regular mode commanding first - AssemblyBase::startTransition(mode, submode); - } -} - -void RwAssembly::handleModeReached() { - if (targetMode == MODE_OFF) { - switcher.turnOff(true); - switcher.doStateMachine(); - // Need to wait with call to AssemblyBase::handleModeReached until power switcher is done - if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) { - AssemblyBase::handleModeReached(); - } - } else { - AssemblyBase::handleModeReached(); - } -} - ReturnValue_t RwAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode) { ReturnValue_t result = returnvalue::OK; object_id_t objId = 0; diff --git a/mission/system/acs/RwAssembly.h b/mission/system/acs/RwAssembly.h index 540e37cc..178595b4 100644 --- a/mission/system/acs/RwAssembly.h +++ b/mission/system/acs/RwAssembly.h @@ -1,8 +1,8 @@ #ifndef MISSION_SYSTEM_RWASS_H_ #define MISSION_SYSTEM_RWASS_H_ -#include #include +#include struct RwHelper { RwHelper(std::array rwIds) : rwIds(rwIds) {} @@ -10,17 +10,15 @@ struct RwHelper { std::array rwIds = {}; }; -class RwAssembly : public AssemblyBase { +class RwAssembly : public SharedPowerAssemblyBase { public: - RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher, + RwAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switchId, RwHelper helper); private: static constexpr uint8_t NUMBER_RWS = 4; RwHelper helper; - PowerSwitcher switcher; bool warningSwitch = true; - bool modeTransitionFailedSwitch = true; FixedArrayList modeTable; ReturnValue_t initialize() override; @@ -35,12 +33,9 @@ class RwAssembly : public AssemblyBase { bool isUseable(object_id_t object, Mode_t mode); // AssemblyBase implementation - void performChildOperation() override; ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override; ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override; ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override; - void startTransition(Mode_t mode, Submode_t submode) override; - void handleModeReached() override; }; #endif /* MISSION_SYSTEM_RWASS_H_ */ diff --git a/mission/system/fdir/RtdFdir.cpp b/mission/system/fdir/RtdFdir.cpp index 008dc405..24ebc2a2 100644 --- a/mission/system/fdir/RtdFdir.cpp +++ b/mission/system/fdir/RtdFdir.cpp @@ -3,4 +3,4 @@ #include "eive/objects.h" RtdFdir::RtdFdir(object_id_t sensorId) - : DeviceHandlerFailureIsolation(sensorId, objects::TCS_BOARD_ASS) {} + : DeviceHandlerFailureIsolation(sensorId, objects::NO_OBJECT) {} diff --git a/mission/system/objects/TcsBoardAssembly.cpp b/mission/system/objects/TcsBoardAssembly.cpp index b32a00a9..2a5a3e7b 100644 --- a/mission/system/objects/TcsBoardAssembly.cpp +++ b/mission/system/objects/TcsBoardAssembly.cpp @@ -4,9 +4,11 @@ #include TcsBoardAssembly::TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, - power::Switch_t theSwitch, TcsBoardHelper helper) - : AssemblyBase(objectId, 24), switcher(pwrSwitcher, theSwitch), helper(helper) { - eventQueue = QueueFactory::instance()->createMessageQueue(24); + power::Switch_t theSwitch, TcsBoardHelper helper, + std::atomic_bool& tcsShortlyUnavailable) + : SharedPowerAssemblyBase(objectId, pwrSwitcher, theSwitch, 16), + helper(helper), + tcsShortlyUnavailable(tcsShortlyUnavailable) { ModeListEntry entry; for (uint8_t idx = 0; idx < NUMBER_RTDS; idx++) { entry.setObject(helper.rtdInfos[idx].first); @@ -16,23 +18,6 @@ TcsBoardAssembly::TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitc } } -void TcsBoardAssembly::performChildOperation() { - auto state = switcher.getState(); - if (state != PowerSwitcher::WAIT_OFF and state != PowerSwitcher::WAIT_ON) { - AssemblyBase::performChildOperation(); - return; - } - switcher.doStateMachine(); - if (state == PowerSwitcher::WAIT_OFF and switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) { - // Indicator that a transition to off is finished - AssemblyBase::handleModeReached(); - } else if (state == PowerSwitcher::WAIT_ON and - switcher.getState() == PowerSwitcher::SWITCH_IS_ON) { - // Indicator that mode commanding can be performed now - AssemblyBase::startTransition(targetMode, targetSubmode); - } -} - ReturnValue_t TcsBoardAssembly::commandChildren(Mode_t mode, Submode_t submode) { ReturnValue_t result = returnvalue::OK; // Initialize the mode table to ensure all devices are in a defined state @@ -50,6 +35,8 @@ ReturnValue_t TcsBoardAssembly::commandChildren(Mode_t mode, Submode_t submode) if (mode == DeviceHandlerIF::MODE_NORMAL or mode == MODE_ON) { result = handleNormalOrOnModeCmd(mode, submode); } + } else { + tcsShortlyUnavailable = true; } HybridIterator tableIter(modeTable.begin(), modeTable.end()); executeTable(tableIter); @@ -94,25 +81,6 @@ ReturnValue_t TcsBoardAssembly::isModeCombinationValid(Mode_t mode, Submode_t su return HasModesIF::INVALID_MODE; } -ReturnValue_t TcsBoardAssembly::initialize() { return AssemblyBase::initialize(); } - -void TcsBoardAssembly::startTransition(Mode_t mode, Submode_t submode) { - if (mode != MODE_OFF) { - switcher.turnOn(true); - switcher.doStateMachine(); - if (switcher.getState() == PowerSwitcher::SWITCH_IS_ON) { - AssemblyBase::startTransition(mode, submode); - } else { - // Need to wait with mode commanding until power switcher is done - targetMode = mode; - targetSubmode = submode; - } - } else { - // Perform regular mode commanding first - AssemblyBase::startTransition(mode, submode); - } -} - ReturnValue_t TcsBoardAssembly::handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode) { ReturnValue_t result = returnvalue::OK; bool needsSecondStep = false; @@ -169,21 +137,6 @@ bool TcsBoardAssembly::isUseable(object_id_t object, Mode_t mode) { return false; } -MessageQueueId_t TcsBoardAssembly::getEventReceptionQueue() { return eventQueue->getId(); } - -void TcsBoardAssembly::handleModeReached() { - if (targetMode == MODE_OFF) { - switcher.turnOff(true); - switcher.doStateMachine(); - // Need to wait with call to AssemblyBase::handleModeReached until power switcher is done - if (switcher.getState() == PowerSwitcher::SWITCH_IS_OFF) { - AssemblyBase::handleModeReached(); - } - } else { - AssemblyBase::handleModeReached(); - } -} - void TcsBoardAssembly::handleChildrenLostMode(ReturnValue_t result) { triggerEvent(CHILDREN_LOST_MODE, result); startTransition(mode, submode); @@ -210,6 +163,12 @@ ReturnValue_t TcsBoardAssembly::checkAndHandleHealthStates(Mode_t deviceMode, return status; } +bool TcsBoardAssembly::checkAndHandleRecovery() { + bool recoveryPending = SharedPowerAssemblyBase::checkAndHandleRecovery(); + tcsShortlyUnavailable = recoveryPending; + return recoveryPending; +} + void TcsBoardAssembly::handleModeTransitionFailed(ReturnValue_t result) { if (targetMode == MODE_OFF) { AssemblyBase::handleModeTransitionFailed(result); diff --git a/mission/system/objects/TcsBoardAssembly.h b/mission/system/objects/TcsBoardAssembly.h index 3c3fc0d6..da10999c 100644 --- a/mission/system/objects/TcsBoardAssembly.h +++ b/mission/system/objects/TcsBoardAssembly.h @@ -4,6 +4,10 @@ #include #include #include +#include +#include + +#include #include "events/subsystemIdRanges.h" #include "returnvalues/classIds.h" @@ -15,23 +19,20 @@ struct TcsBoardHelper { std::array, 16> rtdInfos = {}; }; -class TcsBoardAssembly : public AssemblyBase, public ConfirmsFailuresIF { +class TcsBoardAssembly : public SharedPowerAssemblyBase { public: static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::TCS_BOARD_ASS; static constexpr Event CHILDREN_LOST_MODE = event::makeEvent(SUBSYSTEM_ID, 0, severity::MEDIUM); TcsBoardAssembly(object_id_t objectId, PowerSwitchIF* pwrSwitcher, power::Switch_t switcher, - TcsBoardHelper helper); - - ReturnValue_t initialize() override; + TcsBoardHelper helper, std::atomic_bool& tcsShortlyUnavailable); private: static constexpr uint8_t NUMBER_RTDS = 16; - PowerSwitcher switcher; bool warningSwitch = true; TcsBoardHelper helper; FixedArrayList modeTable; - MessageQueueIF* eventQueue = nullptr; + std::atomic_bool& tcsShortlyUnavailable; ReturnValue_t handleNormalOrOnModeCmd(Mode_t mode, Submode_t submode); /** @@ -42,17 +43,12 @@ class TcsBoardAssembly : public AssemblyBase, public ConfirmsFailuresIF { */ bool isUseable(object_id_t object, Mode_t mode); - // ConfirmFailureIF implementation - MessageQueueId_t getEventReceptionQueue() override; - // AssemblyBase implementation - void performChildOperation() override; ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) override; ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) override; ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) override; - void startTransition(Mode_t mode, Submode_t submode) override; - void handleModeReached() override; ReturnValue_t checkAndHandleHealthStates(Mode_t deviceMode, Submode_t deviceSubmode); + bool checkAndHandleRecovery() override; // These two overrides prevent a transition of the whole assembly back to off just because // some devices are not working diff --git a/mission/tcs/defs.h b/mission/tcs/defs.h new file mode 100644 index 00000000..df868360 --- /dev/null +++ b/mission/tcs/defs.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace tcs { + +extern std::atomic_bool TCS_BOARD_SHORTLY_UNAVAILABLE; + +} diff --git a/tmtc b/tmtc index 43b530cd..f075d289 160000 --- a/tmtc +++ b/tmtc @@ -1 +1 @@ -Subproject commit 43b530cdb7dfe6774962bf4b8a880e1c9a6e6580 +Subproject commit f075d28905b42a018b275d8c640cb0fd9d64dc26 diff --git a/unittest/controller/testThermalController.cpp b/unittest/controller/testThermalController.cpp index 623c6d52..dbe86949 100644 --- a/unittest/controller/testThermalController.cpp +++ b/unittest/controller/testThermalController.cpp @@ -15,6 +15,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") { const object_id_t THERMAL_CONTROLLER_ID = 0x123; + std::atomic_bool tcsBrdShortlyUnavailable = false; TemperatureSensorInserter::Max31865DummyMap map0; TemperatureSensorInserter::Tmp1075DummyMap map1; @@ -29,7 +30,7 @@ TEST_CASE("Thermal Controller", "[ThermalController]") { // testEnvironment::initialize(); - ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler); + ThermalController controller(THERMAL_CONTROLLER_ID, *heaterHandler, tcsBrdShortlyUnavailable); ReturnValue_t result = controller.initialize(); REQUIRE(result == returnvalue::OK);