|
| 1 | +// ====================================================================== |
| 2 | +// \title StartupManager.cpp |
| 3 | +// \author starchmd |
| 4 | +// \brief cpp file for StartupManager component implementation class |
| 5 | +// ====================================================================== |
| 6 | + |
| 7 | +#include "FprimeZephyrReference/Components/StartupManager/StartupManager.hpp" |
| 8 | + |
| 9 | +#include "Os/File.hpp" |
| 10 | + |
| 11 | +namespace Components { |
| 12 | + |
| 13 | +// ---------------------------------------------------------------------- |
| 14 | +// Component construction and destruction |
| 15 | +// ---------------------------------------------------------------------- |
| 16 | + |
| 17 | +StartupManager ::StartupManager(const char* const compName) : StartupManagerComponentBase(compName) {} |
| 18 | + |
| 19 | +StartupManager ::~StartupManager() {} |
| 20 | + |
| 21 | +// ---------------------------------------------------------------------- |
| 22 | +// Handler implementations for typed input ports |
| 23 | +// ---------------------------------------------------------------------- |
| 24 | + |
| 25 | +//! \brief Template function to read a type T from a file at file_path |
| 26 | +//! |
| 27 | +//! This will read a type T with size 'size' from the file located at file_path. It will return SUCCESS if |
| 28 | +//! the read and deserialization were successful, and FAILURE otherwise. |
| 29 | +//! |
| 30 | +//! The file will be opened and closed within this function. value will not be modified by this function unless |
| 31 | +//! the read operation is successful. |
| 32 | +//! |
| 33 | +//! \warning this function is only safe to use for types T with size `size` that fit well in stack memory. |
| 34 | +//! |
| 35 | +//! \param file_path: path to the file to read from |
| 36 | +//! \param value: reference to the variable to read into |
| 37 | +//! \return Status of the read operation |
| 38 | +template <typename T, FwSizeType BUFFER_SIZE> |
| 39 | +StartupManager::Status read(const Fw::StringBase& file_path, T& value) { |
| 40 | + // Create the necessary file and deserializer objects for reading a type from a file |
| 41 | + StartupManager::Status return_status = StartupManager::FAILURE; |
| 42 | + Os::File file; |
| 43 | + U8 data_buffer[BUFFER_SIZE]; |
| 44 | + Fw::ExternalSerializeBuffer deserializer(data_buffer, sizeof(data_buffer)); |
| 45 | + |
| 46 | + // Open the file for reading, and continue only if successful |
| 47 | + Os::File::Status status = file.open(file_path.toChar(), Os::File::OPEN_READ); |
| 48 | + if (status == Os::File::OP_OK) { |
| 49 | + FwSizeType size = sizeof(data_buffer); |
| 50 | + status = file.read(data_buffer, size); |
| 51 | + if (status == Os::File::OP_OK && size == sizeof(data_buffer)) { |
| 52 | + // When the read is successful, and the size is correct then the buffer must absolutely contain the |
| 53 | + // serialized data and thus it is safe to assert on the deserialization status |
| 54 | + deserializer.setBuffLen(size); |
| 55 | + Fw::SerializeStatus serialize_status = deserializer.deserializeTo(value); |
| 56 | + FW_ASSERT(serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK, |
| 57 | + static_cast<FwAssertArgType>(serialize_status)); |
| 58 | + return_status = StartupManager::SUCCESS; |
| 59 | + } |
| 60 | + } |
| 61 | + (void)file.close(); |
| 62 | + return return_status; |
| 63 | +} |
| 64 | + |
| 65 | +//! \brief Template function to write a type T to a file at file_path |
| 66 | +//! |
| 67 | +//! This will write a type T with size 'size' to the file located at file_path. It will return SUCCESS if |
| 68 | +//! the serialization and write were successful, and FAILURE otherwise. |
| 69 | +//! |
| 70 | +//! The file will be opened and closed within this function. |
| 71 | +//! |
| 72 | +//! \warning this function is only safe to use for types T with size `size` that fit well in stack memory. |
| 73 | +//! |
| 74 | +//! \param file_path: path to the file to write to |
| 75 | +//! \param value: reference to the variable to write |
| 76 | +//! \return Status of the write operation |
| 77 | +template <typename T, FwSizeType BUFFER_SIZE> |
| 78 | +StartupManager::Status write(const Fw::StringBase& file_path, const T& value) { |
| 79 | + // Create the necessary file and deserializer objects for reading a type from a file |
| 80 | + StartupManager::Status return_status = StartupManager::FAILURE; |
| 81 | + Os::File file; |
| 82 | + U8 data_buffer[BUFFER_SIZE]; |
| 83 | + |
| 84 | + // Serialize the value into the data buffer. Since the buffer is created here it is safe to assert on the |
| 85 | + // serialization status. |
| 86 | + Fw::ExternalSerializeBuffer serializer(data_buffer, sizeof(data_buffer)); |
| 87 | + Fw::SerializeStatus serialize_status = serializer.serializeFrom(value); |
| 88 | + FW_ASSERT(serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast<FwAssertArgType>(serialize_status)); |
| 89 | + |
| 90 | + // Open the file for writing, and continue only if successful |
| 91 | + Os::File::Status status = file.open(file_path.toChar(), Os::File::OPEN_CREATE, Os::File::OVERWRITE); |
| 92 | + if (status == Os::File::OP_OK) { |
| 93 | + FwSizeType size = sizeof(data_buffer); |
| 94 | + status = file.write(data_buffer, size); |
| 95 | + if (status == Os::File::OP_OK && size == sizeof(data_buffer)) { |
| 96 | + return_status = StartupManager::SUCCESS; |
| 97 | + } |
| 98 | + } |
| 99 | + (void)file.close(); |
| 100 | + return return_status; |
| 101 | +} |
| 102 | + |
| 103 | +FwSizeType StartupManager ::update_boot_count() { |
| 104 | + // Read the boot count file path from parameter and assert that it is either valid or the default value |
| 105 | + FwSizeType boot_count = 0; |
| 106 | + Fw::ParamValid is_valid; |
| 107 | + auto boot_count_file = this->paramGet_BOOT_COUNT_FILE(is_valid); |
| 108 | + FW_ASSERT(is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT); |
| 109 | + |
| 110 | + // Open the boot count file and add one to the current boot count ensuring a minimum of 1 in the case |
| 111 | + // of read failure. Since read will retain the `0` initial value on read failure, we can ignore the error |
| 112 | + // status returned by the read. |
| 113 | + (void)read<FwSizeType, sizeof(FwSizeType)>(boot_count_file, boot_count); |
| 114 | + boot_count = FW_MAX(1, boot_count + 1); |
| 115 | + // Rewrite the updated boot count back to the file, and on failure emit a warning about the inability to |
| 116 | + // persist the boot count. |
| 117 | + StartupManager::Status status = write<FwSizeType, sizeof(FwSizeType)>(boot_count_file, boot_count); |
| 118 | + if (status != StartupManager::SUCCESS) { |
| 119 | + this->log_WARNING_LO_BootCountUpdateFailure(); |
| 120 | + } |
| 121 | + return boot_count; |
| 122 | +} |
| 123 | + |
| 124 | +Fw::Time StartupManager ::update_quiescence_start() { |
| 125 | + Fw::ParamValid is_valid; |
| 126 | + auto time_file = this->paramGet_QUIESCENCE_START_FILE(is_valid); |
| 127 | + FW_ASSERT(is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT); |
| 128 | + |
| 129 | + Fw::Time time = this->getTime(); |
| 130 | + // Open the quiescence start time file and read the current time. On read failure, return the current time. |
| 131 | + StartupManager::Status status = read<Fw::Time, Fw::Time::SERIALIZED_SIZE>(time_file, time); |
| 132 | + // On read failure, write the current time to the file for future reads. This only happens on read failure because |
| 133 | + // there is a singular quiescence start time for the whole mission. |
| 134 | + if (status != StartupManager::SUCCESS) { |
| 135 | + status = write<Fw::Time, Fw::Time::SERIALIZED_SIZE>(time_file, time); |
| 136 | + if (status != StartupManager::SUCCESS) { |
| 137 | + this->log_WARNING_LO_QuiescenceFileInitFailure(); |
| 138 | + } |
| 139 | + } |
| 140 | + return time; |
| 141 | +} |
| 142 | + |
| 143 | +void StartupManager ::completeSequence_handler(FwIndexType portNum, |
| 144 | + FwOpcodeType opCode, |
| 145 | + U32 cmdSeq, |
| 146 | + const Fw::CmdResponse& response) { |
| 147 | + // Respond to the completion status of the start-up sequence |
| 148 | + if (response == Fw::CmdResponse::OK) { |
| 149 | + this->log_ACTIVITY_LO_StartupSequenceFinished(); |
| 150 | + } else { |
| 151 | + this->log_WARNING_LO_StartupSequenceFailed(response); |
| 152 | + } |
| 153 | +} |
| 154 | + |
| 155 | +void StartupManager ::run_handler(FwIndexType portNum, U32 context) { |
| 156 | + Fw::ParamValid is_valid; |
| 157 | + |
| 158 | + // On the first call, update the boot count, set the quiescence start time, and dispatch the start-up sequence |
| 159 | + if (this->m_boot_count == 0) { |
| 160 | + this->m_boot_count = this->update_boot_count(); |
| 161 | + this->m_quiescence_start = this->update_quiescence_start(); |
| 162 | + |
| 163 | + Fw::ParamString first_sequence = this->paramGet_STARTUP_SEQUENCE_FILE(is_valid); |
| 164 | + FW_ASSERT(is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT); |
| 165 | + this->runSequence_out(0, first_sequence); |
| 166 | + } |
| 167 | + |
| 168 | + // Calculate the quiescence end time based on the quiescence period parameter |
| 169 | + Fw::TimeIntervalValue quiescence_period = this->paramGet_QUIESCENCE_TIME(is_valid); |
| 170 | + FW_ASSERT(is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT); |
| 171 | + Fw::Time quiescence_interval(this->m_quiescence_start.getTimeBase(), quiescence_period.get_seconds(), |
| 172 | + quiescence_period.get_useconds()); |
| 173 | + Fw::Time end_time = Fw::Time::add(this->m_quiescence_start, quiescence_interval); |
| 174 | + |
| 175 | + // Are we waiting for quiescence? |
| 176 | + if (this->m_waiting) { |
| 177 | + // Check if the system is armed or if this is not the first boot. In both cases, we skip waiting. |
| 178 | + bool armed = this->paramGet_ARMED(is_valid); |
| 179 | + FW_ASSERT(is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT); |
| 180 | + |
| 181 | + // If not armed or this is not the first boot, we skip waiting |
| 182 | + if (!armed || end_time <= this->getTime()) { |
| 183 | + this->m_waiting = false; |
| 184 | + this->cmdResponse_out(this->m_stored_opcode, this->m_stored_sequence, Fw::CmdResponse::OK); |
| 185 | + } |
| 186 | + } |
| 187 | + this->tlmWrite_QuiescenceEndTime( |
| 188 | + Fw::TimeValue(end_time.getTimeBase(), end_time.getContext(), end_time.getSeconds(), end_time.getUSeconds())); |
| 189 | + this->tlmWrite_BootCount(this->m_boot_count); |
| 190 | +} |
| 191 | + |
| 192 | +// ---------------------------------------------------------------------- |
| 193 | +// Handler implementations for commands |
| 194 | +// ---------------------------------------------------------------------- |
| 195 | + |
| 196 | +void StartupManager ::WAIT_FOR_QUIESCENCE_cmdHandler(FwOpcodeType opCode, U32 cmdSeq) { |
| 197 | + this->m_stored_opcode = opCode; |
| 198 | + this->m_stored_sequence = cmdSeq; |
| 199 | + this->m_waiting = true; |
| 200 | +} |
| 201 | + |
| 202 | +} // namespace Components |
0 commit comments