#ifndef FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ #define FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ #include "../container/FixedArrayList.h" #include "../subsystem/SubsystemBase.h" #include "DeviceHandlerBase.h" /** * @brief Base class to implement reconfiguration and failure handling for * redundant devices by monitoring their modes and health states. * @details * Documentation: Dissertation Baetz p.156, 157. * * This class reduces the complexity of controller components which would * otherwise be needed for the handling of redundant devices. However, it can also be used to * manage the mode keeping and recovery of non-redundant devices * * The template class monitors mode and health state of its children * and checks availability of devices on every detected change. * AssemblyBase does not implement any redundancy logic by itself, but provides * adaptation points for implementations to do so. Since most monitoring * activities rely on mode and health state only and are therefore * generic, it is sufficient for subclasses to provide: * * 1. check logic when active-> checkChildrenStateOn * 2. transition logic to change the mode -> commandChildren * * Important: * * The implementation must call #registerChild for all commanded children during initialization. * The implementation must call the initialization function of the base class. * (This will call the function in SubsystemBase) */ class AssemblyBase : public SubsystemBase { public: static const uint8_t INTERFACE_ID = CLASS_ID::ASSEMBLY_BASE; static const ReturnValue_t NEED_SECOND_STEP = MAKE_RETURN_CODE(0x01); static const ReturnValue_t NEED_TO_RECONFIGURE = MAKE_RETURN_CODE(0x02); static const ReturnValue_t MODE_FALLBACK = MAKE_RETURN_CODE(0x03); static const ReturnValue_t CHILD_NOT_COMMANDABLE = MAKE_RETURN_CODE(0x04); static const ReturnValue_t NEED_TO_CHANGE_HEALTH = MAKE_RETURN_CODE(0x05); static const ReturnValue_t NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE = MAKE_RETURN_CODE(0xa1); AssemblyBase(object_id_t objectId, uint16_t commandQueueDepth = 8); virtual ~AssemblyBase(); protected: /** * Command children to reach [mode,submode] combination. Can be done by setting * #commandsOutstanding correctly, or using #executeTable. In case of an FDIR recovery, * the user needs to ensure that the target devices are healthy. If a device is not healthy, * a recovery might be on-going and the device needs to be commanded to off first. * @param mode * @param submode * @return * - @c returnvalue::OK if ok * - @c NEED_SECOND_STEP if children need to be commanded again */ virtual ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) = 0; /** * Check whether desired assembly mode was achieved by checking the modes * or/and health states of child device handlers. * The assembly template class will also call this function if a health * or mode change of a child device handler was detected. * @param wantedMode * @param wantedSubmode * @return */ virtual ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, Submode_t wantedSubmode) = 0; /** * Check whether a combination of mode and submode is valid. * * Ground Controller like precise return values from HasModesIF. * So, please return any of them. * * @param mode The targeted mode * @param submode The targeted submmode * @return Any information why this combination is invalid from HasModesIF * like HasModesIF::INVALID_SUBMODE. * On success return returnvalue::OK */ virtual ReturnValue_t isModeCombinationValid(Mode_t mode, Submode_t submode) = 0; enum InternalState { STATE_NONE, STATE_OVERWRITE_HEALTH, STATE_NEED_SECOND_STEP, STATE_SINGLE_STEP, STATE_SECOND_STEP, } internalState; enum RecoveryState { RECOVERY_IDLE, RECOVERY_STARTED, RECOVERY_ONGOING, RECOVERY_ONGOING_2, RECOVERY_WAIT } recoveryState; //!< Indicates if one of the children requested a recovery. ChildrenMap::iterator recoveringDevice; /** * the mode the current transition is trying to achieve. * Can be different from the modehelper.commandedMode! */ Mode_t targetMode; /** * the submode the current transition is trying to achieve. * Can be different from the modehelper.commandedSubmode! */ Submode_t targetSubmode; Countdown recoveryOffTimer; static const uint32_t POWER_OFF_TIME_MS = 1000; virtual ReturnValue_t handleCommandMessage(CommandMessage *message); virtual ReturnValue_t handleHealthReply(CommandMessage *message); /** * @brief Default periodic handler * @details * This is the default periodic handler which will be called by the SubsystemBase * performOperation. It performs the child transitions or reacts to changed health/mode states * of children objects */ virtual void performChildOperation() override; /** * This function handles changed mode or health states of children * @return */ bool handleChildrenChanged(); /** * This method is called if the children changed its mode in a way that * the current mode can't be kept. * Default behavior is to go to MODE_OFF. * @param result The failure code which was returned by checkChildrenState. */ virtual void handleChildrenLostMode(ReturnValue_t result); bool handleChildrenChangedHealth(); /** * Core transition handler. The default implementation will only do something if * #commandsOutstanding is smaller or equal to zero, which means that all mode commands * from the #doPerformTransition call were executed successfully. * * Unless a second step was requested, the function will then use #checkChildrenState to * determine whether the target mode was reached. * * There is some special handling for certain (internal) modes: * - A second step is necessary. #commandChildren will be performed again * - The device health was overwritten. #commandChildren will be called * - A recovery is ongoing. #checkAndHandleRecovery will be called. */ virtual void handleChildrenTransition(); ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, uint32_t *msToReachTheMode); /** * Calls #doStartTransition and triggers an informative event as well that the mode will * change * @param mode * @param submode */ virtual void startTransition(Mode_t mode, Submode_t submode); /** * This function starts the transition by setting the internal #targetSubmode and #targetMode * variables and then calling the #commandChildren function. * @param mode * @param submode */ virtual void doStartTransition(Mode_t mode, Submode_t submode); virtual bool isInTransition(); virtual void handleModeReached(); virtual void handleModeTransitionFailed(ReturnValue_t result); void sendHealthCommand(MessageQueueId_t sendTo, HealthState health); virtual ReturnValue_t checkChildrenStateOff(); ReturnValue_t checkChildrenState(); virtual ReturnValue_t checkChildOff(uint32_t objectId); /** * Manages recovery of a device * @return true if recovery is still ongoing, false else. */ virtual bool checkAndHandleRecovery(); /** * Helper method to overwrite health state of one of the children. * Also sets state to STATE_OVERWRITE_HEATH. * @param objectId Must be a registered child. */ void overwriteDeviceHealth(object_id_t objectId, HasHealthIF::HealthState oldHealth); void triggerModeHelperEvents(Mode_t mode, Submode_t submode); }; #endif /* FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ */