diff --git a/CHANGELOG.md b/CHANGELOG.md index ea769898..8ae5eed8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ will consitute of a breaking change warranting a new major release: 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 @@ -35,6 +37,8 @@ will consitute of a breaking change warranting a new major release: 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. - TCS controller now only has an OFF mode and an ON mode +- The TCS controller pauses operations related to the TCS board assembly (reading sensors and + the primary control loop) while a TCS board recovery is on-going. - Allow specifying custom OBSW update filename. This allowed keeping a cleaner file structure where each update has a name including the version - The files extracted during an update process are deleted after the update was performed to keep diff --git a/dummies/helperFactory.cpp b/dummies/helperFactory.cpp index 54ca3e89..5a7d1d5b 100644 --- a/dummies/helperFactory.cpp +++ b/dummies/helperFactory.cpp @@ -40,6 +40,7 @@ #include "mission/system/com/comModeTree.h" #include "mission/system/tree/payloadModeTree.h" #include "mission/system/tree/tcsModeTree.h" +#include "mission/tcs/defs.h" void dummy::createDummies(DummyCfg cfg, PowerSwitchIF& pwrSwitcher, GpioIF* gpioIF) { new ComIFDummy(objects::DUMMY_COM_IF); @@ -204,7 +205,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/fsfw b/fsfw index 285d327b..ffa2fa47 160000 --- a/fsfw +++ b/fsfw @@ -1 +1 @@ -Subproject commit 285d327b97514946f0714e477289f67ee8bd413f +Subproject commit ffa2fa477f105cc876264335d5b25fc9b174a181 diff --git a/linux/ObjectFactory.cpp b/linux/ObjectFactory.cpp index 66a3195b..72f30278 100644 --- a/linux/ObjectFactory.cpp +++ b/linux/ObjectFactory.cpp @@ -29,6 +29,7 @@ #include "mission/system/acs/acsModeTree.h" #include "mission/system/tree/payloadModeTree.h" #include "mission/system/tree/tcsModeTree.h" +#include "mission/tcs/defs.h" void ObjectFactory::createSunSensorComponents(GpioIF* gpioComIF, SpiComIF* spiComIF, PowerSwitchIF& pwrSwitcher, std::string spiDev, @@ -278,7 +279,8 @@ void ObjectFactory::createRtdComponents(std::string spiDev, GpioIF* gpioComIF, std::array rtds = {}; RtdFdir* rtdFdir = nullptr; - TcsBoardAssembly* tcsBoardAss = ObjectFactory::createTcsBoardAssy(*pwrSwitcher); + TcsBoardAssembly* tcsBoardAss = + ObjectFactory::createTcsBoardAssy(*pwrSwitcher, tcs::TCS_BOARD_SHORTLY_UNAVAILABLE); // Create special low level reader communication interface new Max31865RtdPolling(objects::SPI_RTD_COM_IF, comIF, gpioComIF); diff --git a/mission/cfdp/CMakeLists.txt b/mission/cfdp/CMakeLists.txt index 8b137891..97d7142b 100644 --- a/mission/cfdp/CMakeLists.txt +++ b/mission/cfdp/CMakeLists.txt @@ -1 +1,3 @@ +target_sources( + ${LIB_EIVE_MISSION} PRIVATE CfdpHandler.cpp) diff --git a/mission/cfdp/CfdpHandler.cpp b/mission/cfdp/CfdpHandler.cpp new file mode 100644 index 00000000..902097b6 --- /dev/null +++ b/mission/cfdp/CfdpHandler.cpp @@ -0,0 +1,134 @@ +#include "CfdpHandler.h" + +#include "fsfw/cfdp/pdu/AckPduReader.h" +#include "fsfw/cfdp/pdu/PduHeaderReader.h" +#include "fsfw/globalfunctions/arrayprinter.h" +#include "fsfw/ipc/QueueFactory.h" +#include "fsfw/tmtcservices/TmTcMessage.h" + +using namespace returnvalue; +using namespace cfdp; + +CfdpHandler::CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg) + : SystemObject(fsfwParams.objectId), + msgQueue(fsfwParams.msgQueue), + destHandler( + DestHandlerParams(LocalEntityCfg(cfdpCfg.id, cfdpCfg.indicCfg, cfdpCfg.faultHandler), + cfdpCfg.userHandler, cfdpCfg.remoteCfgProvider, cfdpCfg.packetInfoList, + cfdpCfg.lostSegmentsList), + FsfwParams(fsfwParams.packetDest, nullptr, this, fsfwParams.tcStore, + fsfwParams.tmStore)) { + destHandler.setMsgQueue(msgQueue); +} + +[[nodiscard]] const char* CfdpHandler::getName() const { return "CFDP Handler"; } + +[[nodiscard]] uint32_t CfdpHandler::getIdentifier() const { + return destHandler.getDestHandlerParams().cfg.localId.getValue(); +} + +[[nodiscard]] MessageQueueId_t CfdpHandler::getRequestQueue() const { return msgQueue.getId(); } + +ReturnValue_t CfdpHandler::initialize() { + ReturnValue_t result = destHandler.initialize(); + if (result != OK) { + return result; + } + tcStore = destHandler.getTcStore(); + tmStore = destHandler.getTmStore(); + + return SystemObject::initialize(); +} + +ReturnValue_t CfdpHandler::performOperation(uint8_t operationCode) { + // TODO: Receive TC packets and route them to source and dest handler, depending on which is + // correct or more appropriate + ReturnValue_t status; + ReturnValue_t result = OK; + TmTcMessage tmtcMsg; + for (status = msgQueue.receiveMessage(&tmtcMsg); status == returnvalue::OK; + status = msgQueue.receiveMessage(&tmtcMsg)) { + result = handleCfdpPacket(tmtcMsg); + if (result != OK) { + status = result; + } + } + auto& fsmRes = destHandler.performStateMachine(); + // TODO: Error handling? + while (fsmRes.callStatus == CallStatus::CALL_AGAIN) { + destHandler.performStateMachine(); + // TODO: Error handling? + } + return status; +} + +ReturnValue_t CfdpHandler::handleCfdpPacket(TmTcMessage& msg) { + auto accessorPair = tcStore->getData(msg.getStorageId()); + if (accessorPair.first != OK) { + return accessorPair.first; + } + PduHeaderReader reader(accessorPair.second.data(), accessorPair.second.size()); + ReturnValue_t result = reader.parseData(); + if (result != returnvalue::OK) { + return INVALID_PDU_FORMAT; + } + // The CFDP distributor should have taken care of ensuring the destination ID is correct + PduType type = reader.getPduType(); + // Only the destination handler can process these PDUs + if (type == PduType::FILE_DATA) { + // Disable auto-deletion of packet + accessorPair.second.release(); + PacketInfo info(type, msg.getStorageId()); + result = destHandler.passPacket(info); + } else { + // Route depending on PDU type and directive type if applicable. It retrieves directive type + // from the raw stream for better performance (with sanity and directive code check). + // The routing is based on section 4.5 of the CFDP standard which specifies the PDU forwarding + // procedure. + + // PDU header only. Invalid supplied data. A directive packet should have a valid data field + // with at least one byte being the directive code + const uint8_t* pduDataField = reader.getPduDataField(); + if (pduDataField == nullptr) { + return INVALID_PDU_FORMAT; + } + if (not FileDirectiveReader::checkFileDirective(pduDataField[0])) { + return INVALID_DIRECTIVE_FIELD; + } + auto directive = static_cast(pduDataField[0]); + + auto passToDestHandler = [&]() { + accessorPair.second.release(); + PacketInfo info(type, msg.getStorageId(), directive); + result = destHandler.passPacket(info); + }; + auto passToSourceHandler = [&]() { + + }; + if (directive == FileDirective::METADATA or directive == FileDirective::EOF_DIRECTIVE or + directive == FileDirective::PROMPT) { + // Section b) of 4.5.3: These PDUs should always be targeted towards the file receiver a.k.a. + // the destination handler + passToDestHandler(); + } else if (directive == FileDirective::FINISH or directive == FileDirective::NAK or + directive == FileDirective::KEEP_ALIVE) { + // Section c) of 4.5.3: These PDUs should always be targeted towards the file sender a.k.a. + // the source handler + passToSourceHandler(); + } else if (directive == FileDirective::ACK) { + // Section a): Recipient depends of the type of PDU that is being acknowledged. We can simply + // extract the PDU type from the raw stream. If it is an EOF PDU, this packet is passed to + // the source handler, for a Finished PDU, it is passed to the destination handler. + FileDirective ackedDirective; + if (not AckPduReader::checkAckedDirectiveField(pduDataField[1], ackedDirective)) { + return INVALID_ACK_DIRECTIVE_FIELDS; + } + if (ackedDirective == FileDirective::EOF_DIRECTIVE) { + passToSourceHandler(); + } else if (ackedDirective == FileDirective::FINISH) { + passToDestHandler(); + } + } + } + return result; +} diff --git a/mission/cfdp/CfdpHandler.h b/mission/cfdp/CfdpHandler.h new file mode 100644 index 00000000..2de9f7dd --- /dev/null +++ b/mission/cfdp/CfdpHandler.h @@ -0,0 +1,71 @@ +#ifndef FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H +#define FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H + +#include + +#include "fsfw/cfdp/handler/DestHandler.h" +#include "fsfw/objectmanager/SystemObject.h" +#include "fsfw/tasks/ExecutableObjectIF.h" +#include "fsfw/tmtcservices/AcceptsTelecommandsIF.h" +#include "fsfw/tmtcservices/TmTcMessage.h" + +struct FsfwHandlerParams { + FsfwHandlerParams(object_id_t objectId, HasFileSystemIF& vfs, AcceptsTelemetryIF& packetDest, + StorageManagerIF& tcStore, StorageManagerIF& tmStore, MessageQueueIF& msgQueue) + : objectId(objectId), + vfs(vfs), + packetDest(packetDest), + tcStore(tcStore), + tmStore(tmStore), + msgQueue(msgQueue) {} + object_id_t objectId{}; + HasFileSystemIF& vfs; + AcceptsTelemetryIF& packetDest; + StorageManagerIF& tcStore; + StorageManagerIF& tmStore; + MessageQueueIF& msgQueue; +}; + +struct CfdpHandlerCfg { + CfdpHandlerCfg(cfdp::EntityId localId, cfdp::IndicationCfg indicationCfg, + cfdp::UserBase& userHandler, cfdp::FaultHandlerBase& userFaultHandler, + cfdp::PacketInfoListBase& packetInfo, cfdp::LostSegmentsListBase& lostSegmentsList, + cfdp::RemoteConfigTableIF& remoteCfgProvider) + : id(std::move(localId)), + indicCfg(indicationCfg), + packetInfoList(packetInfo), + lostSegmentsList(lostSegmentsList), + remoteCfgProvider(remoteCfgProvider), + userHandler(userHandler), + faultHandler(userFaultHandler) {} + + cfdp::EntityId id; + cfdp::IndicationCfg indicCfg; + cfdp::PacketInfoListBase& packetInfoList; + cfdp::LostSegmentsListBase& lostSegmentsList; + cfdp::RemoteConfigTableIF& remoteCfgProvider; + cfdp::UserBase& userHandler; + cfdp::FaultHandlerBase& faultHandler; +}; + +class CfdpHandler : public SystemObject, public ExecutableObjectIF, public AcceptsTelecommandsIF { + public: + explicit CfdpHandler(const FsfwHandlerParams& fsfwParams, const CfdpHandlerCfg& cfdpCfg); + + [[nodiscard]] const char* getName() const override; + [[nodiscard]] uint32_t getIdentifier() const override; + [[nodiscard]] MessageQueueId_t getRequestQueue() const override; + + ReturnValue_t initialize() override; + ReturnValue_t performOperation(uint8_t operationCode) override; + + private: + MessageQueueIF& msgQueue; + cfdp::DestHandler destHandler; + StorageManagerIF* tcStore = nullptr; + StorageManagerIF* tmStore = nullptr; + + ReturnValue_t handleCfdpPacket(TmTcMessage& msg); +}; + +#endif // FSFW_EXAMPLE_HOSTED_CFDPHANDLER_H diff --git a/mission/controller/ThermalController.cpp b/mission/controller/ThermalController.cpp index 74581166..b12088ae 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(); + } } } { @@ -188,7 +192,7 @@ void ThermalController::performControlOperation() { } else { transitionWhenHeatersOffCycles++; } - } else if (mode != MODE_OFF) { + } else if (mode != MODE_OFF and not tcsBrdShortlyUnavailable) { performThermalModuleCtrl(heaterSwitchStateArray); } } diff --git a/mission/controller/ThermalController.h b/mission/controller/ThermalController.h index 7466674b..7aa05b1e 100644 --- a/mission/controller/ThermalController.h +++ b/mission/controller/ThermalController.h @@ -22,6 +22,7 @@ #include #include +#include #include /** @@ -101,7 +102,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; @@ -188,6 +190,10 @@ class ThermalController : public ExtendedControllerBase { susMax1227::SusDataset susSet10; susMax1227::SusDataset susSet11; + // If the TCS board in unavailable, for example due to a recovery, skip + // some TCS controller tasks to avoid unnecessary events. + const std::atomic_bool& tcsBrdShortlyUnavailable = false; + lp_var_t tempQ7s = lp_var_t(objects::CORE_CONTROLLER, core::PoolIds::TEMPERATURE); lp_var_t battTemp1 = lp_var_t(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_1); lp_var_t battTemp2 = lp_var_t(objects::BPX_BATT_HANDLER, bpxBat::BATT_TEMP_2); diff --git a/mission/genericFactory.cpp b/mission/genericFactory.cpp index ce338097..cbef2f77 100644 --- a/mission/genericFactory.cpp +++ b/mission/genericFactory.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include #include @@ -48,6 +48,7 @@ #include "mission/system/acs/RwAssembly.h" #include "mission/system/acs/acsModeTree.h" #include "mission/system/tree/tcsModeTree.h" +#include "mission/tcs/defs.h" #include "mission/tmtc/tmFilters.h" #include "objects/systemObjectList.h" #include "tmtc/pusIds.h" @@ -90,6 +91,8 @@ EiveFaultHandler EIVE_FAULT_HANDLER; } // namespace cfdp +std::atomic_bool tcs::TCS_BOARD_SHORTLY_UNAVAILABLE = false; + void ObjectFactory::produceGenericObjects(HealthTableIF** healthTable_, PusTmFunnel** pusFunnel, CfdpTmFunnel** cfdpFunnel, SdCardMountedIF& sdcMan, StorageManagerIF** ipcStore, StorageManagerIF** tmStore, @@ -300,7 +303,8 @@ void ObjectFactory::createGenericHeaterComponents(GpioIF& gpioIF, PowerSwitchIF& } void ObjectFactory::createThermalController(HeaterHandler& heaterHandler) { - auto* tcsCtrl = new ThermalController(objects::THERMAL_CONTROLLER, heaterHandler); + auto* tcsCtrl = new ThermalController(objects::THERMAL_CONTROLLER, heaterHandler, + tcs::TCS_BOARD_SHORTLY_UNAVAILABLE); tcsCtrl->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); } void ObjectFactory::createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch, @@ -366,10 +370,12 @@ void ObjectFactory::createAcsBoardAssy(PowerSwitchIF& pwrSwitcher, acsAss->connectModeTreeParent(satsystem::acs::ACS_SUBSYSTEM); } -TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher) { +TcsBoardAssembly* ObjectFactory::createTcsBoardAssy(PowerSwitchIF& pwrSwitcher, + std::atomic_bool& tcsShortlyUnavailable) { TcsBoardHelper helper(RTD_INFOS); - auto* tcsBoardAss = new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher, - power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper); + auto* tcsBoardAss = + new TcsBoardAssembly(objects::TCS_BOARD_ASS, &pwrSwitcher, + power::Switches::PDU1_CH0_TCS_BOARD_3V3, helper, tcsShortlyUnavailable); tcsBoardAss->connectModeTreeParent(satsystem::tcs::SUBSYSTEM); return tcsBoardAss; } diff --git a/mission/genericFactory.h b/mission/genericFactory.h index 496c516b..6cd2068d 100644 --- a/mission/genericFactory.h +++ b/mission/genericFactory.h @@ -55,7 +55,8 @@ void createRwAssy(PowerSwitchIF& pwrSwitcher, power::Switch_t theSwitch, void createSusAssy(PowerSwitchIF& pwrSwitcher, std::array suses); void createAcsBoardAssy(PowerSwitchIF& pwrSwitcher, std::array assemblyDhbs, ExtendedControllerBase* gpsCtrl, GpioIF* gpioComIF); -TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher); +TcsBoardAssembly* createTcsBoardAssy(PowerSwitchIF& pwrSwitcher, + std::atomic_bool& tcsShortlyUnavailable); } // namespace ObjectFactory diff --git a/mission/power/defs.h b/mission/power/defs.h index b10fe1ff..13e5e63f 100644 --- a/mission/power/defs.h +++ b/mission/power/defs.h @@ -50,6 +50,7 @@ static constexpr Event FDIR_REACTION_IGNORED = event::makeEvent(SUBSYSTEM_ID, 3, enum class States { IDLE, SWITCHING_POWER, CHECKING_POWER, MODE_COMMANDING }; enum class OpCodes { NONE, TO_OFF_DONE, TO_NOT_OFF_DONE, TIMEOUT_OCCURED }; +enum RecoveryCustomStates { IDLE, POWER_SWITCHING_OFF, POWER_SWITCHING_ON, DONE }; } // namespace power namespace duallane { diff --git a/mission/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 index bab3d862..cb1a73e4 100644 --- a/mission/tcs/defs.h +++ b/mission/tcs/defs.h @@ -1,6 +1,6 @@ -#ifndef MISSION_TCS_DEFS_H_ -#define MISSION_TCS_DEFS_H_ +#pragma once +#include #include namespace heater { @@ -17,4 +17,8 @@ enum Switch : uint8_t { }; } -#endif /* MISSION_TCS_DEFS_H_ */ +namespace tcs { + +extern std::atomic_bool TCS_BOARD_SHORTLY_UNAVAILABLE; + +} diff --git a/unittest/controller/testThermalController.cpp b/unittest/controller/testThermalController.cpp index 5ee41ce2..0e12d94d 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);