@@ -21,89 +21,120 @@ StartupManager ::~StartupManager() {}
2121// Handler implementations for typed input ports
2222// ----------------------------------------------------------------------
2323
24- FwSizeType StartupManager ::update_boot_count () {
25- // Read the boot count file path from parameter and assert that it is either valid or the default value
26- Fw::ParamValid is_valid;
27- auto boot_count_file = this ->paramGet_BOOT_COUNT_FILE (is_valid);
28- FW_ASSERT (is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT);
29-
30- // Open the boot count file and read the current boot count
31- FwSizeType boot_count = 0 ;
24+ // ! \brief Template function to read a type T from a file at file_path
25+ // !
26+ // ! This will read a type T with size 'size' from the file located at file_path. It will return SUCCESS if
27+ // ! the read and deserialization were successful, and FAILURE otherwise.
28+ // !
29+ // ! The file will be opened and closed within this function. value will not be modified by this function unless
30+ // ! the read operation is successful.
31+ // !
32+ // ! \warning this function is only safe to use for types T with size `size` that fit well in stack memory.
33+ // !
34+ // ! \param file_path: path to the file to read from
35+ // ! \param value: reference to the variable to read into
36+ // ! \return Status of the read operation
37+ template <typename T, FwSizeType BUFFER_SIZE>
38+ StartupManager::Status read (const Fw::StringBase& file_path, T& value) {
39+ // Create the necessary file and deserializer objects for reading a type from a file
40+ StartupManager::Status return_status = StartupManager::FAILURE;
3241 Os::File file;
33- Os::File::Status status = file. open (boot_count_file. toChar (), Os::File::OPEN_READ) ;
34- U8 buffer[ sizeof (FwSizeType)] ;
42+ U8 data_buffer[BUFFER_SIZE] ;
43+ Fw::ExternalSerializeBuffer deserializer (data_buffer, sizeof (data_buffer)) ;
3544
36- // If the file read ok, then we read the boot count. Otherwise, we assume boot count is zero thus making
37- // this the first boot.
45+ // Open the file for reading, and continue only if successful
46+ Os::File::Status status = file. open (file_path. toChar (), Os::File::OPEN_READ);
3847 if (status == Os::File::OP_OK) {
39- FwSizeType size = sizeof (buffer);
40- status = file.read (buffer, size);
41- if (status == Os::File::OP_OK) {
42- Fw::ExternalSerializeBuffer buffer_obj (buffer, sizeof (buffer));
43- Fw::SerializeStatus serialization_status = buffer_obj.deserializeTo (boot_count);
44- if (serialization_status != Fw::SerializeStatus::FW_SERIALIZE_OK) {
45- boot_count = 0 ; // Default to zero if deserialization fails
46- }
48+ FwSizeType size = sizeof (data_buffer);
49+ status = file.read (data_buffer, size);
50+ if (status == Os::File::OP_OK && size == sizeof (data_buffer)) {
51+ // When the read is successful, and the size is correct then the buffer must absolutely contain the
52+ // serialized data and thus it is safe to assert on the deserialization status
53+ deserializer.setBuffLen (size);
54+ Fw::SerializeStatus serialize_status = deserializer.deserializeTo (value);
55+ FW_ASSERT (serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK,
56+ static_cast <FwAssertArgType>(serialize_status));
57+ return_status = StartupManager::SUCCESS;
4758 }
48- file.close ();
4959 }
50- // Boot count of zero is a flag value, so ensure a minimum boot count of 1
51- boot_count = FW_MAX (1 , boot_count + 1 );
60+ (void )file.close ();
61+ return return_status;
62+ }
5263
53- // Open the file for writing the boot count
54- status = file.open (boot_count_file.toChar (), Os::File::OPEN_CREATE, Os::File::OVERWRITE);
64+ // ! \brief Template function to write a type T to a file at file_path
65+ // !
66+ // ! This will write a type T with size 'size' to the file located at file_path. It will return SUCCESS if
67+ // ! the serialization and write were successful, and FAILURE otherwise.
68+ // !
69+ // ! The file will be opened and closed within this function.
70+ // !
71+ // ! \warning this function is only safe to use for types T with size `size` that fit well in stack memory.
72+ // !
73+ // ! \param file_path: path to the file to write to
74+ // ! \param value: reference to the variable to write
75+ // ! \return Status of the write operation
76+ template <typename T, FwSizeType BUFFER_SIZE>
77+ StartupManager::Status write (const Fw::StringBase& file_path, const T& value) {
78+ // Create the necessary file and deserializer objects for reading a type from a file
79+ StartupManager::Status return_status = StartupManager::FAILURE;
80+ Os::File file;
81+ U8 data_buffer[BUFFER_SIZE];
82+
83+ // Serialize the value into the data buffer. Since the buffer is created here it is safe to assert on the
84+ // serialization status.
85+ Fw::ExternalSerializeBuffer serializer (data_buffer, sizeof (data_buffer));
86+ Fw::SerializeStatus serialize_status = serializer.serializeFrom (value);
87+ FW_ASSERT (serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK, static_cast <FwAssertArgType>(serialize_status));
88+
89+ // Open the file for writing, and continue only if successful
90+ Os::File::Status status = file.open (file_path.toChar (), Os::File::OPEN_CREATE, Os::File::OVERWRITE);
5591 if (status == Os::File::OP_OK) {
56- Fw::ExternalSerializeBuffer buffer_obj (buffer, sizeof (FwSizeType));
57- Fw::SerializeStatus serialize_status = buffer_obj.serializeFrom (boot_count);
58- // Write only when the serialization was successful
59- if (serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK) {
60- FwSizeType size = sizeof (buffer);
61- (void )file.write (buffer, size);
92+ FwSizeType size = sizeof (data_buffer);
93+ status = file.write (data_buffer, size);
94+ if (status == Os::File::OP_OK && size == sizeof (data_buffer)) {
95+ return_status = StartupManager::SUCCESS;
6296 }
63- file.close ();
6497 }
65- return boot_count;
98+ (void )file.close ();
99+ return return_status;
66100}
67101
68- Fw::Time StartupManager ::get_quiescence_start () {
69- // Read the quiescence start time file path from parameter and assert that it is either valid or the default value
102+ FwSizeType StartupManager ::update_boot_count () {
103+ // Read the boot count file path from parameter and assert that it is either valid or the default value
104+ FwSizeType boot_count = 0 ;
70105 Fw::ParamValid is_valid;
106+ auto boot_count_file = this ->paramGet_BOOT_COUNT_FILE (is_valid);
107+ FW_ASSERT (is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT);
71108
109+ // Open the boot count file and add one to the current boot count ensuring a minimum of 1 in the case
110+ // of read failure. Since read will retain the `0` initial value on read failure, we can ignore the error
111+ // status returned by the read.
112+ (void )read<FwSizeType, sizeof (FwSizeType)>(boot_count_file, boot_count);
113+ boot_count = FW_MAX (1 , boot_count + 1 );
114+ // Rewrite the updated boot count back to the file, and on failure emit a warning about the inability to
115+ // persist the boot count.
116+ StartupManager::Status status = write<FwSizeType, sizeof (FwSizeType)>(boot_count_file, boot_count);
117+ if (status != StartupManager::SUCCESS) {
118+ this ->log_WARNING_LO_BootCountUpdateFailure ();
119+ }
120+ return boot_count;
121+ }
122+
123+ Fw::Time StartupManager ::update_quiescence_start () {
124+ Fw::ParamValid is_valid;
72125 auto time_file = this ->paramGet_QUIESCENCE_START_FILE (is_valid);
73126 FW_ASSERT (is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT);
74127
75- // Open the boot count file and read the current boot count
76- Fw::Time time;
77- Os::File file;
78- Os::File::Status status = file.open (time_file.toChar (), Os::File::OPEN_READ);
79- U8 buffer[Fw::Time::SERIALIZED_SIZE];
80-
81- // If the file read ok, then we read the quiescence start time.
82- if (status == Os::File::OP_OK) {
83- FwSizeType size = sizeof (buffer);
84- status = file.read (buffer, size);
85- if (status == Os::File::OP_OK) {
86- Fw::ExternalSerializeBuffer buffer_obj (buffer, sizeof (buffer));
87- Fw::SerializeStatus serialization_status = buffer_obj.deserializeTo (time);
88- if (serialization_status != Fw::SerializeStatus::FW_SERIALIZE_OK) {
89- time = this ->getTime (); // Default to current time if deserialization fails
90- }
91- }
92- (void )file.close ();
93- }
94- // Write quiescence start time if read failed
95- if (status != Os::File::OP_OK) {
96- FwSizeType size = sizeof (buffer);
97- time = this ->getTime ();
98- status = file.open (time_file.toChar (), Os::File::OPEN_CREATE, Os::File::OVERWRITE);
99- if (status == Os::File::OP_OK) {
100- Fw::ExternalSerializeBuffer buffer_obj (buffer, sizeof (Fw::Time::SERIALIZED_SIZE));
101- Fw::SerializeStatus serialize_status = buffer_obj.serializeFrom (time);
102- if (serialize_status == Fw::SerializeStatus::FW_SERIALIZE_OK) {
103- (void )file.write (buffer, size);
104- }
128+ Fw::Time time = this ->getTime ();
129+ // Open the quiescence start time file and read the current time. On read failure, return the current time.
130+ StartupManager::Status status = read<Fw::Time, Fw::Time::SERIALIZED_SIZE>(time_file, time);
131+ // On read failure, write the current time to the file for future reads. This only happens on read failure because
132+ // there is a singular quiescence start time for the whole mission.
133+ if (status != StartupManager::SUCCESS) {
134+ status = write<Fw::Time, Fw::Time::SERIALIZED_SIZE>(time_file, time);
135+ if (status != StartupManager::SUCCESS) {
136+ this ->log_WARNING_LO_QuiescenceFileInitFailure ();
105137 }
106- (void )file.close ();
107138 }
108139 return time;
109140}
@@ -112,7 +143,12 @@ void StartupManager ::completeSequence_handler(FwIndexType portNum,
112143 FwOpcodeType opCode,
113144 U32 cmdSeq,
114145 const Fw::CmdResponse& response) {
115- // TODO
146+ // Respond to the completion status of the start-up sequence
147+ if (response == Fw::CmdResponse::OK) {
148+ this ->log_ACTIVITY_LO_StartupSequenceFinished ();
149+ } else {
150+ this ->log_WARNING_LO_StartupSequenceFailed (response);
151+ }
116152}
117153
118154void StartupManager ::run_handler (FwIndexType portNum, U32 context) {
@@ -121,30 +157,33 @@ void StartupManager ::run_handler(FwIndexType portNum, U32 context) {
121157 // On the first call, update the boot count, set the quiescence start time, and dispatch the start-up sequence
122158 if (this ->m_boot_count == 0 ) {
123159 this ->m_boot_count = this ->update_boot_count ();
124- this ->m_quiescence_start = this ->get_quiescence_start ();
160+ this ->m_quiescence_start = this ->update_quiescence_start ();
125161
126162 Fw::ParamString first_sequence = this ->paramGet_STARTUP_SEQUENCE_FILE (is_valid);
127163 FW_ASSERT (is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT);
128164 this ->runSequence_out (0 , first_sequence);
129165 }
130166
167+ // Calculate the quiescence end time based on the quiescence period parameter
168+ Fw::TimeIntervalValue quiescence_period = this ->paramGet_QUIESCENCE_TIME (is_valid);
169+ Fw::Time quiescence_interval (quiescence_period.get_seconds (), quiescence_period.get_useconds ());
170+ FW_ASSERT (is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT);
171+ Fw::Time end_time = Fw::Time::add (this ->m_quiescence_start , quiescence_interval);
172+
131173 // Are we waiting for quiescence?
132174 if (this ->m_waiting ) {
133175 // Check if the system is armed or if this is not the first boot. In both cases, we skip waiting.
134176 bool armed = this ->paramGet_ARMED (is_valid);
135177 FW_ASSERT (is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT);
136178
137- Fw::TimeIntervalValue quiescence_period = this ->paramGet_QUIESCENCE_TIME (is_valid);
138- Fw::Time quiescence_interval (quiescence_period.get_seconds (), quiescence_period.get_useconds ());
139- FW_ASSERT (is_valid == Fw::ParamValid::VALID || is_valid == Fw::ParamValid::DEFAULT);
140- Fw::Time end_time = Fw::Time::add (this ->m_quiescence_start , quiescence_interval);
141-
142179 // If not armed or this is not the first boot, we skip waiting
143180 if (!armed || end_time <= this ->getTime ()) {
144181 this ->m_waiting = false ;
145182 this ->cmdResponse_out (this ->m_stored_opcode , this ->m_stored_sequence , Fw::CmdResponse::OK);
146183 }
147184 }
185+ this ->tlmWrite_QuiescenceEndTime (
186+ Fw::TimeValue (end_time.getTimeBase (), end_time.getContext (), end_time.getSeconds (), end_time.getUSeconds ()));
148187 this ->tlmWrite_BootCount (this ->m_boot_count );
149188}
150189
0 commit comments