Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
450 changes: 426 additions & 24 deletions FprimeZephyrReference/Components/ModeManager/ModeManager.cpp

Large diffs are not rendered by default.

85 changes: 83 additions & 2 deletions FprimeZephyrReference/Components/ModeManager/ModeManager.fpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
module Components {

@ System mode enumeration
@ System mode enumeration (values ordered for +1/-1 sequential transitions)
enum SystemMode {
NORMAL = 0 @< Normal operational mode
SAFE_MODE = 1 @< Safe mode with non-critical components powered off
NORMAL = 2 @< Normal operational mode
PAYLOAD_MODE = 3 @< Payload mode with payload power and battery enabled
}

@ Reason for entering safe mode (used for recovery decision logic)
enum SafeModeReason {
NONE = 0 @< Not in safe mode or reason cleared
LOW_BATTERY = 1 @< Entered due to low voltage condition
SYSTEM_FAULT = 2 @< Entered due to unintended reboot/system fault
GROUND_COMMAND = 3 @< Entered via ground command
EXTERNAL_REQUEST = 4 @< Entered via external component request
}

@ Port for notifying about mode changes
Expand All @@ -29,6 +39,9 @@ module Components {
@ Port to query the current system mode
sync input port getMode: Components.GetSystemMode

@ Port called before intentional reboot to set clean shutdown flag
sync input port prepareForReboot: Fw.Signal

# ----------------------------------------------------------------------
# Output Ports
# ----------------------------------------------------------------------
Expand Down Expand Up @@ -57,6 +70,14 @@ module Components {
@ Only succeeds if currently in safe mode
sync command EXIT_SAFE_MODE()

@ Command to enter payload mode
@ Only succeeds if currently in normal mode
sync command ENTER_PAYLOAD_MODE()

@ Command to exit payload mode
@ Only succeeds if currently in payload mode
sync command EXIT_PAYLOAD_MODE()

# ----------------------------------------------------------------------
# Events
# ----------------------------------------------------------------------
Expand All @@ -83,6 +104,22 @@ module Components {
severity warning high \
format "External fault detected - external component forced safe mode"

@ Event emitted when entering payload mode
event EnteringPayloadMode(
reason: string size 100 @< Reason for entering payload mode
) \
severity activity high \
format "ENTERING PAYLOAD MODE: {}"

@ Event emitted when exiting payload mode
event ExitingPayloadMode() \
severity activity high \
format "Exiting payload mode"

@ Event emitted when payload mode is manually commanded
event ManualPayloadModeEntry() \
severity activity high \
format "Payload mode entry commanded manually"

@ Event emitted when command validation fails
event CommandValidationFailed(
Expand All @@ -92,6 +129,13 @@ module Components {
severity warning low \
format "Command {} failed: {}"

@ Event emitted when automatically exiting payload mode due to low voltage
event AutoPayloadModeExit(
voltage: F32 @< Voltage that triggered the exit
) \
severity warning high \
format "AUTO EXIT PAYLOAD MODE: Low voltage detected ({}V)"

@ Event emitted when state persistence fails
event StatePersistenceFailure(
operation: string size 20 @< Operation that failed (save/load)
Expand All @@ -100,6 +144,37 @@ module Components {
severity warning low \
format "State persistence {} failed with status {}"

@ Event emitted when automatically entering safe mode due to low voltage
event AutoSafeModeEntry(
reason: SafeModeReason @< Reason for entering safe mode
voltage: F32 @< Voltage that triggered the entry (0 if N/A)
) \
severity warning high \
format "AUTO SAFE MODE ENTRY: reason={} voltage={}V"
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AutoSafeModeEntry event takes two parameters (reason and voltage) as shown in line 149-150, but the event emission in ModeManager.cpp line 98 only passes the reason and voltage correctly. However, the event format string on line 153 shows "reason={} voltage={}V" which will display the enum numeric value (e.g., "1") rather than the enum name ("LOW_BATTERY"). Consider using a formatted string representation or documenting that operators will see numeric enum values.

Suggested change
format "AUTO SAFE MODE ENTRY: reason={} voltage={}V"
format "AUTO SAFE MODE ENTRY: reason={!r} voltage={}V"

Copilot uses AI. Check for mistakes.

@ Event emitted when automatically exiting safe mode due to voltage recovery
event AutoSafeModeExit(
voltage: F32 @< Voltage that triggered recovery
) \
severity activity high \
format "AUTO SAFE MODE EXIT: Voltage recovered to {}V"

@ Event emitted when unintended reboot is detected
event UnintendedRebootDetected() \
severity warning high \
format "UNINTENDED REBOOT DETECTED: Entering safe mode"

@ Event emitted when preparing for intentional reboot
event PreparingForReboot() \
severity activity high \
format "Preparing for intentional reboot - setting clean shutdown flag"

@ Event emitted when external safe mode request is ignored in PAYLOAD_MODE
@ The system must transition PAYLOAD_MODE -> NORMAL -> SAFE_MODE sequentially
event ExternalFaultIgnoredInPayloadMode() \
severity warning low \
format "External fault detected but ignored - in PAYLOAD_MODE (must exit to NORMAL first)"

# ----------------------------------------------------------------------
# Telemetry
# ----------------------------------------------------------------------
Expand All @@ -110,6 +185,12 @@ module Components {
@ Number of times safe mode has been entered
telemetry SafeModeEntryCount: U32

@ Number of times payload mode has been entered
telemetry PayloadModeEntryCount: U32

@ Current safe mode reason (NONE if not in safe mode)
telemetry CurrentSafeModeReason: SafeModeReason


###############################################################################
# Standard AC Ports: Required for Channels, Events, Commands, and Parameters #
Expand Down
80 changes: 69 additions & 11 deletions FprimeZephyrReference/Components/ModeManager/ModeManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ class ModeManager : public ModeManagerComponentBase {
Components::SystemMode getMode_handler(FwIndexType portNum //!< The port number
) override;

//! Handler implementation for prepareForReboot
//!
//! Port called before intentional reboot to set clean shutdown flag
void prepareForReboot_handler(FwIndexType portNum //!< The port number
) override;

// ----------------------------------------------------------------------
// Handler implementations for commands
// ----------------------------------------------------------------------
Expand All @@ -70,6 +76,16 @@ class ModeManager : public ModeManagerComponentBase {
U32 cmdSeq //!< The command sequence number
) override;

//! Handler implementation for command ENTER_PAYLOAD_MODE
void ENTER_PAYLOAD_MODE_cmdHandler(FwOpcodeType opCode, //!< The opcode
U32 cmdSeq //!< The command sequence number
) override;

//! Handler implementation for command EXIT_PAYLOAD_MODE
void EXIT_PAYLOAD_MODE_cmdHandler(FwOpcodeType opCode, //!< The opcode
U32 cmdSeq //!< The command sequence number
) override;

private:
// ----------------------------------------------------------------------
// Private helper methods
Expand All @@ -81,15 +97,35 @@ class ModeManager : public ModeManagerComponentBase {
//! Save persistent state to file
void saveState();

//! Enter safe mode with optional reason override
void enterSafeMode(const char* reason = nullptr);
//! Enter safe mode with specified reason
void enterSafeMode(Components::SafeModeReason reason);

//! Exit safe mode
//! Exit safe mode (manual command)
void exitSafeMode();

//! Exit safe mode automatically due to voltage recovery
//! Only allowed when safe mode reason is LOW_BATTERY
void exitSafeModeAutomatic(F32 voltage);

//! Enter payload mode with optional reason override
void enterPayloadMode(const char* reason = nullptr);

//! Exit payload mode (manual)
void exitPayloadMode();

//! Exit payload mode automatically due to fault condition
//! More aggressive than manual exit - turns off all switches
void exitPayloadModeAutomatic(F32 voltage);

//! Turn off non-critical components
void turnOffNonCriticalComponents();

//! Turn on payload (load switches 6 & 7)
void turnOnPayload();

//! Turn off payload (load switches 6 & 7)
void turnOffPayload();

//! Turn on components (restore normal operation)
void turnOnComponents();

Expand All @@ -103,24 +139,46 @@ class ModeManager : public ModeManagerComponentBase {
// Private enums and types
// ----------------------------------------------------------------------

//! System mode enumeration
enum class SystemMode : U8 { NORMAL = 0, SAFE_MODE = 1 };
//! System mode enumeration (values ordered for +1/-1 sequential transitions)
enum class SystemMode : U8 { SAFE_MODE = 1, NORMAL = 2, PAYLOAD_MODE = 3 };

//! Persistent state structure
//! Persistent state structure (v2: includes safe mode reason and clean shutdown flag)
struct PersistentState {
U8 mode; //!< Current mode (SystemMode)
U32 safeModeEntryCount; //!< Number of times safe mode entered
U8 mode; //!< Current mode (SystemMode)
U32 safeModeEntryCount; //!< Number of times safe mode entered
U32 payloadModeEntryCount; //!< Number of times payload mode entered
U8 safeModeReason; //!< Reason for current safe mode (SafeModeReason)
U8 cleanShutdown; //!< Flag indicating if last shutdown was intentional (1=clean, 0=unclean)
};
Comment on lines +145 to 152
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "Persistent state structure (v2: includes safe mode reason and clean shutdown flag)" but there's no versioning mechanism in the actual struct or persistence code. If the struct format changes in the future, old state files will be silently corrupted or misread. Consider adding a version field to the PersistentState struct and validating it during loadState() to handle format migrations safely.

Copilot uses AI. Check for mistakes.

// ----------------------------------------------------------------------
// Private member variables
// ----------------------------------------------------------------------

SystemMode m_mode; //!< Current system mode
U32 m_safeModeEntryCount; //!< Counter for safe mode entries
U32 m_runCounter; //!< Counter for run handler calls (1Hz)
SystemMode m_mode; //!< Current system mode
U32 m_safeModeEntryCount; //!< Counter for safe mode entries
U32 m_payloadModeEntryCount; //!< Counter for payload mode entries
U32 m_runCounter; //!< Counter for run handler calls (1Hz)
U32 m_lowVoltageCounter; //!< Counter for consecutive low voltage readings (payload mode exit)

// Safe mode specific state
Components::SafeModeReason m_safeModeReason; //!< Reason for current safe mode entry
U32 m_safeModeVoltageCounter; //!< Counter for consecutive low voltage readings (safe mode entry)
U32 m_recoveryVoltageCounter; //!< Counter for consecutive high voltage readings (safe mode exit)

static constexpr const char* STATE_FILE_PATH = "/mode_state.bin"; //!< State file path

// Voltage threshold constants for payload mode protection
static constexpr F32 LOW_VOLTAGE_THRESHOLD = 7.2f; //!< Voltage threshold for payload mode exit
static constexpr U32 LOW_VOLTAGE_DEBOUNCE_SECONDS = 10; //!< Consecutive seconds below threshold

// Voltage threshold constants for safe mode entry/exit (Normal <-> Safe)
static constexpr F32 SAFE_MODE_ENTRY_VOLTAGE = 6.7f; //!< Voltage threshold for safe mode entry
static constexpr F32 SAFE_MODE_RECOVERY_VOLTAGE = 8.0f; //!< Voltage threshold for safe mode auto-recovery
static constexpr U32 SAFE_MODE_DEBOUNCE_SECONDS = 10; //!< Consecutive seconds for safe mode transitions

// Buffer size for reason strings (must match FPP string size definitions)
static constexpr FwSizeType REASON_STRING_SIZE = 100; //!< Matches FPP reason: string size 100
};

} // namespace Components
Expand Down
Loading