Skip to content

Commit 04eb690

Browse files
LeStarchineskhou
andauthored
Adds in Startup Manager and File Handling Components (#74)
* Add start-up manager * Added the FileManager stuff * Final start stuff * Add startup manager * added the Config * Add command sequencer * Add back in startup manager * Working file persistence * fixed time interval problem * sdd for startup manager * added the specific sequences and commands to add the sequences * updates seauncesing instructions * Bump versions * Fix parameters, com aggregation * updated docs/spelling * removed excessive print statements * appease linter * uped the hash buckets --------- Co-authored-by: ineskhou <[email protected]>
1 parent f97253f commit 04eb690

32 files changed

+815
-225
lines changed

CMakeLists.txt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,5 @@ fprime_setup_included_code()
3434

3535
# This includes project-wide objects
3636
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FprimeZephyrReference")
37-
if (FPRIME_USE_POSIX)
38-
set_target_properties(Drv_Ip PROPERTIES EXCLUDE_FROM_ALL TRUE)
39-
set_target_properties(Drv_TcpServer PROPERTIES EXCLUDE_FROM_ALL TRUE)
40-
set_target_properties(Drv_TcpClient PROPERTIES EXCLUDE_FROM_ALL TRUE)
41-
set_target_properties(Drv_Udp PROPERTIES EXCLUDE_FROM_ALL TRUE)
42-
endif()
4337
set_target_properties(Svc_FatalHandler PROPERTIES EXCLUDE_FROM_ALL TRUE)
4438
set_target_properties(fprime-zephyr_Drv_ZephyrSpiDriver PROPERTIES EXCLUDE_FROM_ALL TRUE)

FprimeZephyrReference/Components/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ImuManager/")
1111
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/NullPrmDb/")
1212
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PowerMonitor/")
1313
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Watchdog")
14+
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/StartupManager/")
1415
add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LoadSwitch/")

FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace Drv {
1111
// Component construction and destruction
1212
// ----------------------------------------------------------------------
1313

14-
RtcManager ::RtcManager(const char* const compName) : RtcManagerComponentBase(compName) {
14+
RtcManager ::RtcManager(const char* const compName) : RtcManagerComponentBase(compName), m_console_throttled(false) {
1515
// Initialize device
1616
this->dev = device_get_binding("RV3028");
1717
}
@@ -27,7 +27,12 @@ void RtcManager ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) {
2727
if (!device_is_ready(this->dev)) {
2828
// Use logger instead of events since this fn is in a critical path for FPrime
2929
// to get time. Events require time, if this method fails an event will fail.
30-
Fw::Logger::log("RTC not ready");
30+
//
31+
// Throttle this message to prevent console flooding and program delays
32+
if (!this->m_console_throttled) {
33+
this->m_console_throttled = true;
34+
Fw::Logger::log("RTC not ready\n");
35+
}
3136
return;
3237
}
3338

FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.fpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ module Drv {
3131
##############################################################################
3232

3333
@ DeviceNotReady event indicates that the RTC is not ready
34-
event DeviceNotReady() severity warning high id 0 format "RTC not ready" throttle 5
34+
event DeviceNotReady() severity warning high id 0 format "RTC not ready" throttle 1
3535

3636
@ TimeSet event indicates that the time was set successfully
3737
event TimeSet(

FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@
77
#define Components_RtcManager_HPP
88

99
#include <Fw/Logger/Logger.hpp>
10+
#include <atomic>
1011
#include <cerrno>
11-
#include <string>
12-
#include <vector>
1312

1413
#include "FprimeZephyrReference/Components/Drv/RtcManager/RtcManagerComponentAc.hpp"
1514
#include <zephyr/device.h>
@@ -62,6 +61,7 @@ class RtcManager final : public RtcManagerComponentBase {
6261
// ----------------------------------------------------------------------
6362
// Private helper methods
6463
// ----------------------------------------------------------------------
64+
std::atomic<bool> m_console_throttled; //!< Counter for console throttle
6565

6666
//! Validate time data
6767
bool timeDataIsValid(Drv::TimeData t);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
####
2+
# F Prime CMakeLists.txt:
3+
#
4+
# SOURCES: list of source files (to be compiled)
5+
# AUTOCODER_INPUTS: list of files to be passed to the autocoders
6+
# DEPENDS: list of libraries that this module depends on
7+
#
8+
# More information in the F´ CMake API documentation:
9+
# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/
10+
#
11+
####
12+
13+
# Module names are derived from the path from the nearest project/library/framework
14+
# root when not specifically overridden by the developer. i.e. The module defined by
15+
# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`.
16+
17+
register_fprime_library(
18+
AUTOCODER_INPUTS
19+
"${CMAKE_CURRENT_LIST_DIR}/StartupManager.fpp"
20+
SOURCES
21+
"${CMAKE_CURRENT_LIST_DIR}/StartupManager.cpp"
22+
# DEPENDS
23+
# MyPackage_MyOtherModule
24+
)
25+
26+
### Unit Tests ###
27+
# register_fprime_ut(
28+
# AUTOCODER_INPUTS
29+
# "${CMAKE_CURRENT_LIST_DIR}/StartupManager.fpp"
30+
# SOURCES
31+
# "${CMAKE_CURRENT_LIST_DIR}/test/ut/StartupManagerTestMain.cpp"
32+
# "${CMAKE_CURRENT_LIST_DIR}/test/ut/StartupManagerTester.cpp"
33+
# DEPENDS
34+
# STest # For rules-based testing
35+
# UT_AUTO_HELPERS
36+
# )
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
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
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
module Components {
2+
@ Manages the start-up sequencing
3+
passive component StartupManager {
4+
@ Check RTC time diff
5+
sync input port run: Svc.Sched
6+
7+
@ Port for sending sequence dispatches
8+
output port runSequence: Svc.CmdSeqIn
9+
10+
@ Port for receiving the status of the start-up sequence
11+
sync input port completeSequence: Fw.CmdResponse
12+
13+
@ Command to wait for system quiescence before proceeding with start-up
14+
sync command WAIT_FOR_QUIESCENCE()
15+
16+
@ Telemetry for boot count
17+
telemetry BootCount: FwSizeType update on change
18+
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+
38+
@ Whether the start-up manager is armed to wait for quiescence
39+
param ARMED: bool default true
40+
41+
@ Time to wait before allowing start-up to proceed
42+
param QUIESCENCE_TIME: Fw.TimeIntervalValue default {seconds = 45 * 60, useconds = 0}
43+
44+
@ File storing the quiescence start time
45+
param QUIESCENCE_START_FILE: string default "/quiescence_start.bin"
46+
47+
@ Path to the start-up sequence file
48+
param STARTUP_SEQUENCE_FILE: string default "/startup.bin"
49+
50+
@ File to store the boot count
51+
param BOOT_COUNT_FILE: string default "/boot_count.bin"
52+
53+
###############################################################################
54+
# Standard AC Ports: Required for Channels, Events, Commands, and Parameters #
55+
###############################################################################
56+
@ Port for requesting the current time
57+
time get port timeCaller
58+
59+
@ Port to return the value of a parameter
60+
param get port prmGetOut
61+
62+
@Port to set the value of a parameter
63+
param set port prmSetOut
64+
65+
@ Port for sending command registrations
66+
command reg port cmdRegOut
67+
68+
@ Port for receiving commands
69+
command recv port cmdIn
70+
71+
@ Port for sending command responses
72+
command resp port cmdResponseOut
73+
74+
@ Port for sending telemetry channels to downlink
75+
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+
83+
}
84+
}

0 commit comments

Comments
 (0)