Skip to content

Commit c8f7078

Browse files
committed
Working file persistence
1 parent f28b4ed commit c8f7078

File tree

4 files changed

+161
-76
lines changed

4 files changed

+161
-76
lines changed

FprimeZephyrReference/Components/StartupManager/StartupManager.cpp

Lines changed: 112 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -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

118154
void 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

FprimeZephyrReference/Components/StartupManager/StartupManager.fpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module Components {
77
@ Port for sending sequence dispatches
88
output port runSequence: Svc.CmdSeqIn
99

10+
@ Port for receiving the status of the start-up sequence
1011
sync input port completeSequence: Fw.CmdResponse
1112

1213
@ Command to wait for system quiescence before proceeding with start-up
@@ -15,6 +16,25 @@ module Components {
1516
@ Telemetry for boot count
1617
telemetry BootCount: FwSizeType update on change
1718

19+
@ Telemetry for quiescence end time
20+
telemetry QuiescenceEndTime: Fw.TimeValue update on change
21+
22+
@ Event emitted when failing to update the boot count file
23+
event BootCountUpdateFailure() severity warning low \
24+
format "Failed to update boot count file"
25+
26+
@ Event emitted when the quiescence file was not updated
27+
event QuiescenceFileInitFailure() severity warning low \
28+
format "Failed to initialize quiescence start time file"
29+
30+
@ Event emitted when the start-up sequence succeeds
31+
event StartupSequenceFinished() severity activity low \
32+
format "Start-up sequence finished successfully"
33+
34+
@ Event emitted when the start-up sequence fails
35+
event StartupSequenceFailed(response: Fw.CmdResponse @< Response code
36+
) severity warning low format "Start-up sequence failed with response code {}"
37+
1838
@ Whether the start-up manager is armed to wait for quiescence
1939
param ARMED: bool default true
2040

@@ -53,5 +73,12 @@ module Components {
5373

5474
@ Port for sending telemetry channels to downlink
5575
telemetry port tlmOut
76+
77+
@ Port for sending textual representation of events
78+
text event port logTextOut
79+
80+
@ Port for sending events to downlink
81+
event port logOut
82+
5683
}
5784
}

FprimeZephyrReference/Components/StartupManager/StartupManager.hpp

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ namespace Components {
1414

1515
class StartupManager final : public StartupManagerComponentBase {
1616
public:
17+
enum Status {
18+
SUCCESS,
19+
FAILURE,
20+
};
1721
// ----------------------------------------------------------------------
1822
// Component construction and destruction
1923
// ----------------------------------------------------------------------
@@ -25,11 +29,25 @@ class StartupManager final : public StartupManagerComponentBase {
2529
//! Destroy StartupManager object
2630
~StartupManager();
2731

28-
// Update and return the boot count
32+
//! \brief read and increment the boot count
33+
//!
34+
//! Reads the boot count from the boot count file, increments it, and writes it back to the file. If the read
35+
//! fails, the boot count will be initialized to 1. If the write fails, a warning will be emitted.
36+
//!
37+
//! \warning this function will modify the boot count file on disk.
38+
//!
39+
//! \return The updated boot count
2940
FwSizeType update_boot_count();
3041

31-
// Get the quiescence start time
32-
Fw::Time get_quiescence_start();
42+
//! \brief get and possibly initialize the quiescence start time
43+
//!
44+
//! Reads the quiescence start time from the quiescence start time file. If the read fails, the current time is
45+
//! written to the file and returned.
46+
//!
47+
//! \warning this function will modify the quiescence start time file on disk if it does not already exist.
48+
//!
49+
//! \return The quiescence start time
50+
Fw::Time update_quiescence_start();
3351

3452
private:
3553
// ----------------------------------------------------------------------

FprimeZephyrReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ telemetry packets ReferenceDeploymentPackets {
1010
ReferenceDeployment.rateGroup10Hz.RgMaxTime
1111
ReferenceDeployment.rateGroup1Hz.RgMaxTime
1212
ReferenceDeployment.startupManager.BootCount
13+
ReferenceDeployment.startupManager.QuiescenceEndTime
1314
}
1415

1516
packet HealthWarnings id 2 group 1 {

0 commit comments

Comments
 (0)