diff --git a/FprimeZephyrReference/Components/Drv/CMakeLists.txt b/FprimeZephyrReference/Components/Drv/CMakeLists.txt index cfcd92d7..69454247 100644 --- a/FprimeZephyrReference/Components/Drv/CMakeLists.txt +++ b/FprimeZephyrReference/Components/Drv/CMakeLists.txt @@ -1,5 +1,5 @@ -# Include project-wide components here add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Helpers/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Lis2mdlManager/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Lsm6dsoManager/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/RtcManager") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Types/") diff --git a/FprimeZephyrReference/Components/Drv/RtcManager/CMakeLists.txt b/FprimeZephyrReference/Components/Drv/RtcManager/CMakeLists.txt new file mode 100644 index 00000000..e6063a25 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/RtcManager/CMakeLists.txt @@ -0,0 +1,36 @@ +#### +# F Prime CMakeLists.txt: +# +# SOURCES: list of source files (to be compiled) +# AUTOCODER_INPUTS: list of files to be passed to the autocoders +# DEPENDS: list of libraries that this module depends on +# +# More information in the F´ CMake API documentation: +# https://fprime.jpl.nasa.gov/latest/docs/reference/api/cmake/API/ +# +#### + +# Module names are derived from the path from the nearest project/library/framework +# root when not specifically overridden by the developer. i.e. The module defined by +# `Ref/SignalGen/CMakeLists.txt` will be named `Ref_SignalGen`. + +register_fprime_library( + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/RtcManager.fpp" + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/RtcManager.cpp" +# DEPENDS +# MyPackage_MyOtherModule +) + +### Unit Tests ### +# register_fprime_ut( +# AUTOCODER_INPUTS +# "${CMAKE_CURRENT_LIST_DIR}/RtcManager.fpp" +# SOURCES +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/RtcManagerTestMain.cpp" +# "${CMAKE_CURRENT_LIST_DIR}/test/ut/RtcManagerTester.cpp" +# DEPENDS +# STest # For rules-based testing +# UT_AUTO_HELPERS +# ) diff --git a/FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.cpp b/FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.cpp new file mode 100644 index 00000000..65b4f3e8 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.cpp @@ -0,0 +1,156 @@ +// ====================================================================== +// \title RtcManager.cpp +// \brief cpp file for RtcManager component implementation class +// ====================================================================== + +#include "FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.hpp" + +namespace Drv { + +// ---------------------------------------------------------------------- +// Component construction and destruction +// ---------------------------------------------------------------------- + +RtcManager ::RtcManager(const char* const compName) : RtcManagerComponentBase(compName) { + // Initialize device + this->dev = device_get_binding("RV3028"); +} + +RtcManager ::~RtcManager() {} + +// ---------------------------------------------------------------------- +// Handler implementations for typed input ports +// ---------------------------------------------------------------------- + +void RtcManager ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) { + // Check device readiness + if (!device_is_ready(this->dev)) { + // Use logger instead of events since this fn is in a critical path for FPrime + // to get time. Events require time, if this method fails an event will fail. + Fw::Logger::log("RTC not ready"); + return; + } + + // Get time from RTC + struct rtc_time time_rtc = {}; + rtc_get_time(this->dev, &time_rtc); + + // Convert to generic tm struct + struct tm* time_tm = rtc_time_to_tm(&time_rtc); + + // Convert to time_t (seconds since epoch) + errno = 0; + time_t seconds = timeutil_timegm(time_tm); + if (errno == ERANGE) { + Fw::Logger::log("RTC returned invalid time"); + return; + } + + // Get microseconds from system clock cycles + // Note: RV3028 does not provide sub-second precision, so this is + // just an approximation based on system cycles. + // FPrime expects microseconds in the range [0, 999999] + uint32_t useconds = k_cyc_to_us_near32(k_cycle_get_32()) % 1000000; + + // Set FPrime time object + time.set(TimeBase::TB_WORKSTATION_TIME, 0, static_cast(seconds), static_cast(useconds)); +} + +// ---------------------------------------------------------------------- +// Handler implementations for commands +// ---------------------------------------------------------------------- + +void RtcManager ::TIME_SET_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, Drv::TimeData t) { + // Check device readiness + if (!device_is_ready(this->dev)) { + // Emit device not ready event + this->log_WARNING_HI_DeviceNotReady(); + + // Send command response + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); + return; + } + this->log_WARNING_HI_DeviceNotReady_ThrottleClear(); + + // Validate time data + if (!this->timeDataIsValid(t)) { + // Emit time not set event + this->log_WARNING_HI_TimeNotSet(); + + // Send command response + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::VALIDATION_ERROR); + return; + } + + // Store current time for logging + Fw::Time time_before_set = this->getTime(); + + // Populate rtc_time structure from TimeData + const struct rtc_time time_rtc = { + .tm_sec = static_cast(t.get_Second()), + .tm_min = static_cast(t.get_Minute()), + .tm_hour = static_cast(t.get_Hour()), + .tm_mday = static_cast(t.get_Day()), + .tm_mon = static_cast(t.get_Month() - 1), // month [0-11] + .tm_year = static_cast(t.get_Year() - 1900), // year since 1900 + .tm_wday = 0, + .tm_yday = 0, + .tm_isdst = 0, + }; + + // Set time on RTC + const int status = rtc_set_time(this->dev, &time_rtc); + + if (status != 0) { + // Emit time not set event + this->log_WARNING_HI_TimeNotSet(); + + // Send command response + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); + return; + } + + // Emit time set event, include previous time for reference + this->log_ACTIVITY_HI_TimeSet(time_before_set.getSeconds(), time_before_set.getUSeconds()); + + // Send command response + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +bool RtcManager ::timeDataIsValid(Drv::TimeData t) { + bool valid = true; + + if (t.get_Year() < 1900) { + this->log_WARNING_HI_YearValidationFailed(t.get_Year()); + valid = false; + } + + if (t.get_Month() < 1 || t.get_Month() > 12) { + this->log_WARNING_HI_MonthValidationFailed(t.get_Month()); + valid = false; + } + + if (t.get_Day() < 1 || t.get_Day() > 31) { + this->log_WARNING_HI_DayValidationFailed(t.get_Day()); + valid = false; + } + + if (t.get_Hour() > 23) { + this->log_WARNING_HI_HourValidationFailed(t.get_Hour()); + valid = false; + } + + if (t.get_Minute() > 59) { + this->log_WARNING_HI_MinuteValidationFailed(t.get_Minute()); + valid = false; + } + + if (t.get_Second() > 59) { + this->log_WARNING_HI_SecondValidationFailed(t.get_Second()); + valid = false; + } + + return valid; +} + +} // namespace Drv diff --git a/FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.fpp b/FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.fpp new file mode 100644 index 00000000..c0876e02 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.fpp @@ -0,0 +1,96 @@ +# Type definition +module Drv { + struct TimeData { + Year: U32 @< Year value. + Month: U32 @< Month value. + Day: U32 @< Day value. + Hour: U32 @< Hour value. + Minute: U32 @< Minute value. + Second: U32 @< Second value. + } +} + +# Port definition +module Drv { + port TimeSet(t: TimeData) + port TimeGet -> U32 +} + +module Drv { + @ Manages the real time clock + passive component RtcManager { + import Svc.Time + + @ TIME_SET command to set the time on the RTC + sync command TIME_SET( + t: Drv.TimeData @< Set the time + ) opcode 0 + + ############################################################################## + #### Uncomment the following examples to start customizing your component #### + ############################################################################## + + @ DeviceNotReady event indicates that the RTC is not ready + event DeviceNotReady() severity warning high id 0 format "RTC not ready" throttle 5 + + @ TimeSet event indicates that the time was set successfully + event TimeSet( + seconds: U32 @< Seconds since epoch + useconds: U32 @< Microseconds + ) severity activity high id 3 format "Time set on RTC, previous time: {}.{}" + + @ TimeNotSet event indicates that the time was not set successfully + event TimeNotSet() severity warning high id 4 format "Time not set on RTC" + + @ YearValidationFailed event indicates that the provided year is invalid + event YearValidationFailed( + year: U32 @< The invalid year + ) severity warning high id 5 format "Provided year is invalid should be >= 1900: {}" + + @ MonthValidationFailed event indicates that the provided month is invalid + event MonthValidationFailed( + month: U32 @< The invalid month + ) severity warning high id 6 format "Provided month is invalid should be in [1, 12]: {}" + + @ DayValidationFailed event indicates that the provided day is invalid + event DayValidationFailed( + day: U32 @< The invalid day + ) severity warning high id 7 format "Provided day is invalid should be in [1, 31]: {}" + + @ HourValidationFailed event indicates that the provided hour is invalid + event HourValidationFailed( + hour: U32 @< The invalid hour + ) severity warning high id 8 format "Provided hour is invalid should be in [0, 23]: {}" + + @ MinuteValidationFailed event indicates that the provided minute is invalid + event MinuteValidationFailed( + minute: U32 @< The invalid minute + ) severity warning high id 9 format "Provided minute is invalid should be in [0, 59]: {}" + + @ SecondValidationFailed event indicates that the provided second is invalid + event SecondValidationFailed( + second: U32 @< The invalid second + ) severity warning high id 10 format "Provided second is invalid should be in [0, 59]: {}" + + ############################################################################### + # Standard AC Ports: Required for Channels, Events, Commands, and Parameters # + ############################################################################### + @ Port for requesting the current time + time get port timeCaller + + @ Port for sending command registrations + command reg port cmdRegOut + + @ Port for receiving commands + command recv port cmdIn + + @ Port for sending command responses + command resp port cmdResponseOut + + @ Port for sending textual representation of events + text event port logTextOut + + @ Port for sending events to downlink + event port logOut + } +} diff --git a/FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.hpp b/FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.hpp new file mode 100644 index 00000000..48b3467b --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/RtcManager/RtcManager.hpp @@ -0,0 +1,77 @@ +// ====================================================================== +// \title RtcManager.hpp +// \brief hpp file for RtcManager component implementation class +// ====================================================================== + +#ifndef Components_RtcManager_HPP +#define Components_RtcManager_HPP + +#include "FprimeZephyrReference/Components/Drv/RtcManager/RtcManagerComponentAc.hpp" + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace Drv { + +class RtcManager final : public RtcManagerComponentBase { + public: + // ---------------------------------------------------------------------- + // Component construction and destruction + // ---------------------------------------------------------------------- + + //! Construct RtcManager object + RtcManager(const char* const compName //!< The component name + ); + + //! Destroy RtcManager object + ~RtcManager(); + + private: + // ---------------------------------------------------------------------- + // Handler implementations for typed input ports + // ---------------------------------------------------------------------- + + //! Handler implementation for timeGetPort + //! + //! Port to retrieve time + void timeGetPort_handler(FwIndexType portNum, //!< The port number + Fw::Time& time //!< Reference to Time object + ) override; + + private: + // ---------------------------------------------------------------------- + // Handler implementations for commands + // ---------------------------------------------------------------------- + + //! Handler implementation for command TIME_SET + //! + //! TIME_SET command to set the time on the RTC + void TIME_SET_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + Drv::TimeData t //!< Set the time + ) override; + + private: + // ---------------------------------------------------------------------- + // Private helper methods + // ---------------------------------------------------------------------- + + //! Validate time data + bool timeDataIsValid(Drv::TimeData t); + + //! device stores the initialized Zephyr RTC device + const struct device* dev; +}; + +} // namespace Drv + +#endif diff --git a/FprimeZephyrReference/Components/Drv/RtcManager/docs/sdd.md b/FprimeZephyrReference/Components/Drv/RtcManager/docs/sdd.md new file mode 100644 index 00000000..47f64a43 --- /dev/null +++ b/FprimeZephyrReference/Components/Drv/RtcManager/docs/sdd.md @@ -0,0 +1,168 @@ +# Components::RtcManager + +The RTC Manager component interfaces with the RTC Real Time Clock (RTC) to provide time measurements. + +### Typical Usage + +#### `TIME_SET` Command Usage +1. The component is instantiated and initialized during system startup +2. A ground station sends a `TIME_SET` command with the desired time +3. On each command, the component: + - Sets the time on the RTC + - Emits a `TimeSet` event if the time is set successfully + - Emits a `TimeNotSet` event if the time is not set successfully + - Emits a `DeviceNotReady` event if the device is not ready + +#### `timeGetPort` Port Usage +1. The component is instantiated and initialized during system startup +2. In a deployment topology, a `time connection` relation is made. +3. On each call, the component: + - Fetches and returns the time from the RTC + - Emits a `DeviceNotReady` event if the device is not ready + +#### `timeGet` Port Usage +1. The component is instantiated and initialized during system startup +2. A manager calls the `timeGet` ports +3. On each call, the component: + - Fetches and returns the time from the RTC + - Emits a `DeviceNotReady` event if the device is not ready + +## Requirements +| Name | Description | Validation | +|---|---|---| +| RtcManager-001 | The RTC Manager has a command that sets the time on the RTC | Integration test | +| RtcManager-002 | The RTC Manager has a port which, when called, set the time in FPrime | Integration test | +| RtcManager-003 | A device not ready event is emitted if the RTC is not ready | Manual | +| RtcManager-004 | A time set event is emitted if the time is set successfully | Integration test | +| RtcManager-005 | A time not set event is emitted if the time is not set successfully | Integration test | + +## Port Descriptions +| Name | Description | +|---|---| +| timeGetPort | Time port for FPrime topology connection to get the time from the RTC | + +## Commands +| Name | Description | +|---|---| +| SET_TIME | Sets the time on the RTC | + +## Events +| Name | Description | +|---|---| +| DeviceNotReady | Emits on unsuccessful device connection | +| TimeSet | Emits on successful time set | +| TimeNotSet | Emits on unsuccessful time set | + +## Class Diagram +```mermaid +classDiagram + namespace Drv { + class RtcManagerComponentBase { + <> + } + class RtcManager { + - dev: device* + + RtcManager(char* compName) + + ~RtcManager() + - void timeGetPort_handler(FwIndexType portNum, Fw::Time& time) + - Fw::CmdResponse timeSet_cmdHandler(const FwOpcodeType opCode, const U32 cmdSeq, const Drv::TimeData& time) + - Fw::Time timeGet(U32& posix_time, U32& u_secs) + } + } + RtcManagerComponentBase <|-- RtcManager : inherits +``` + +## Sequence Diagrams + +### `timeGetPort` port + +The `timeGetPort` port is called from a `time connection` in a deployment topology to sync the RTC's time with FPrime's internal clock. + +#### Success +```mermaid +sequenceDiagram + participant Deployment Time Connection + participant RTC Manager + participant Zephyr Time API + participant RTC + Deployment Time Connection-->>RTC Manager: Call timeGetPort time port + RTC Manager->>Zephyr Time API: Read time + Zephyr Time API->>RTC: Read time + RTC->>Zephyr Time API: Return time + Zephyr Time API->>RTC Manager: Return time + RTC Manager-->>Deployment Time Connection: Return time +``` + +#### Device Not Ready +```mermaid +sequenceDiagram + participant Event Log + participant Deployment Time Connection + participant RTC Manager + participant Zephyr Time API + participant RTC + Deployment Time Connection->>RTC Manager: Call timeGetPort time port + RTC Manager->>Zephyr Time API: Read time + Zephyr Time API->>RTC: Read time + RTC->>Zephyr Time API: Return device not ready + Zephyr Time API->>RTC Manager: Return device not ready + RTC Manager->>Event Log: Emit DeviceNotReady event + RTC Manager->>Deployment Time Connection: Return 0 time +``` + +### `TIME_SET` Command + +The `TIME_SET` command is called to set the current time on the RTC. + +#### Success +```mermaid +sequenceDiagram + participant Ground Station + participant Event Log + participant RTC Manager + participant Zephyr Time API + participant RTC + Ground Station-->>RTC Manager: Command to set time with Drv::TimeData struct + RTC Manager->>Zephyr Time API: Set time + Zephyr Time API->>RTC: Set time + RTC->>Zephyr Time API: Return set success + Zephyr Time API->>RTC Manager: Return set success + RTC Manager->>Event Log: Emit event TimeSet +``` + +#### Device Not Ready +```mermaid +sequenceDiagram + participant Ground Station + participant Event Log + participant RTC Manager + participant Zephyr Time API + participant RTC + Ground Station-->>RTC Manager: Command to set time with Drv::TimeData struct + RTC Manager->>Zephyr Time API: Set time + Zephyr Time API->>RTC: Set time + RTC->>Zephyr Time API: Return device not ready + Zephyr Time API->>RTC Manager: Return device not ready + RTC Manager->>Event Log: Emit event DeviceNotReady +``` + +#### Time Not Set +```mermaid +sequenceDiagram + participant Ground Station + participant Event Log + participant RTC Manager + participant Zephyr Time API + participant RTC + Ground Station-->>RTC Manager: Command to set time with Drv::TimeData struct + RTC Manager->>Zephyr Time API: Set time + Zephyr Time API->>RTC: Set time + RTC->>Zephyr Time API: Return set failure + Zephyr Time API->>RTC Manager: Return set failure + RTC Manager->>Event Log: Emit event TimeNotSet +``` + +## Change Log +| Date | Description | +|---|---| +| 2025-9-18 | Initial RTC Manager component | diff --git a/FprimeZephyrReference/ReferenceDeployment/Main.cpp b/FprimeZephyrReference/ReferenceDeployment/Main.cpp index 209a863b..e3148ff0 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Main.cpp +++ b/FprimeZephyrReference/ReferenceDeployment/Main.cpp @@ -4,12 +4,11 @@ // // ====================================================================== // Used to access topology functions -// clang-format off -// Keep the includes in this order for Zephyr + #include -#include + #include -// clang-format on +#include const struct device* serial = DEVICE_DT_GET(DT_NODELABEL(cdc_acm_uart0)); diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp b/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp index 597c813b..7358f8f7 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/instances.fpp @@ -49,20 +49,19 @@ module ReferenceDeployment { # ---------------------------------------------------------------------- # Passive component instances # ---------------------------------------------------------------------- + instance rateGroupDriver: Svc.RateGroupDriver base id 0x10010000 - instance chronoTime: Svc.ChronoTime base id 0x10010000 + instance version: Svc.Version base id 0x10011000 - instance rateGroupDriver: Svc.RateGroupDriver base id 0x10011000 + instance timer: Zephyr.ZephyrRateDriver base id 0x10012000 - instance version: Svc.Version base id 0x10012000 + instance comDriver: Zephyr.ZephyrUartDriver base id 0x10013000 - instance timer: Zephyr.ZephyrRateDriver base id 0x10013000 + instance gpioDriver: Zephyr.ZephyrGpioDriver base id 0x10014000 - instance comDriver: Zephyr.ZephyrUartDriver base id 0x10014000 + instance watchdog: Components.Watchdog base id 0x10015000 - instance gpioDriver: Zephyr.ZephyrGpioDriver base id 0x10015000 - - instance watchdog: Components.Watchdog base id 0x10016000 + instance rtcManager: Drv.RtcManager base id 0x10016000 instance imuManager: Components.ImuManager base id 0x10017000 diff --git a/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp b/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp index 60afd964..5ee6be88 100644 --- a/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp +++ b/FprimeZephyrReference/ReferenceDeployment/Top/topology.fpp @@ -20,7 +20,6 @@ module ReferenceDeployment { # ---------------------------------------------------------------------- # Instances used in the topology # ---------------------------------------------------------------------- - instance chronoTime instance rateGroup10Hz instance rateGroup1Hz instance rateGroupDriver @@ -29,6 +28,7 @@ module ReferenceDeployment { instance gpioDriver instance watchdog instance prmDb + instance rtcManager instance imuManager instance lis2mdlManager instance lsm6dsoManager @@ -41,7 +41,7 @@ module ReferenceDeployment { event connections instance CdhCore.events text event connections instance CdhCore.textLogger health connections instance CdhCore.$health - time connections instance chronoTime + time connections instance rtcManager telemetry connections instance CdhCore.tlmSend param connections instance prmDb @@ -109,10 +109,6 @@ module ReferenceDeployment { imuManager.temperatureGet -> lsm6dsoManager.temperatureGet } - connections ReferenceDeployment { - - } - } } diff --git a/FprimeZephyrReference/test/int/rtc_test.py b/FprimeZephyrReference/test/int/rtc_test.py new file mode 100644 index 00000000..d0770248 --- /dev/null +++ b/FprimeZephyrReference/test/int/rtc_test.py @@ -0,0 +1,177 @@ +""" +rtc_test.py: + +Integration tests for the RTC Manager component. +""" + +import json +import time +from datetime import datetime, timezone + +import pytest +from fprime.common.models.serialize.numerical_types import U32Type +from fprime.common.models.serialize.time_type import TimeType +from fprime_gds.common.data_types.ch_data import ChData +from fprime_gds.common.data_types.event_data import EventData +from fprime_gds.common.testing_fw.api import IntegrationTestAPI + + +@pytest.fixture(autouse=True) +def set_now_time(fprime_test_api: IntegrationTestAPI): + """Fixture to set the time to test runner's time after each test""" + yield + set_time(fprime_test_api) + fprime_test_api.clear_histories() + + +def set_time(fprime_test_api: IntegrationTestAPI, dt: datetime = None): + """Helper function to set the time to now or to a specified datetime""" + if dt is None: + dt = datetime.now(timezone.utc) + + time_data = dict( + Year=dt.year, + Month=dt.month, + Day=dt.day, + Hour=dt.hour, + Minute=dt.minute, + Second=dt.second, + ) + time_data_str = json.dumps(time_data) + fprime_test_api.send_and_assert_command( + "ReferenceDeployment.rtcManager.TIME_SET", + [ + time_data_str, + ], + max_delay=2, + ) + fprime_test_api.assert_event("ReferenceDeployment.rtcManager.TimeSet", timeout=2) + + +def test_01_time_set(fprime_test_api: IntegrationTestAPI): + """Test that we can set the time""" + + # Set time to Curiosity landing on Mars (7 minutes of terror! https://youtu.be/Ki_Af_o9Q9s) + curiosity_landing = datetime(2012, 8, 6, 5, 17, 57, tzinfo=timezone.utc) + set_time(fprime_test_api, curiosity_landing) + + # Fetch event data + result: EventData = fprime_test_api.assert_event( + "ReferenceDeployment.rtcManager.TimeSet", timeout=2 + ) + + # Fetch previously set time from event args + event_previous_time_arg: U32Type = result.args[0] + previously_set_time = datetime.fromtimestamp( + event_previous_time_arg.val, tz=timezone.utc + ) + + # Ensure microseconds are included in event + microseconds_arg: U32Type = result.args[1] + assert 0 <= microseconds_arg.val < 100_000_000, ( + "Microseconds arg should be >= 0 and < 1 million" + ) + + # Fetch FPrime time from event + fp_time: TimeType = result.get_time() + event_time = datetime.fromtimestamp(fp_time.seconds, tz=timezone.utc) + + # Assert previously set time is within 30 seconds of now + pytest.approx(previously_set_time, abs=30) == datetime.now(timezone.utc) + + # Assert event time is within 30 seconds of curiosity landing + pytest.approx(event_time, abs=30) == curiosity_landing + + # Fetch event data + result: EventData = fprime_test_api.assert_event( + "ReferenceDeployment.rtcManager.TimeSet", timeout=2 + ) + + # Assert time is within 30 seconds of now + pytest.approx(event_time, abs=30) == datetime.now(timezone.utc) + + +def test_02_time_incrementing(fprime_test_api: IntegrationTestAPI): + """Test that time increments over time""" + + # Fetch initial time + result: ChData = fprime_test_api.assert_telemetry( + "CdhCore.cmdDisp.CommandsDispatched", timeout=3 + ) + + # Convert FPrime time to datetime + fp_time: TimeType = result.time + initial_time = datetime.fromtimestamp(fp_time.seconds, tz=timezone.utc) + + # Wait for time to increment + fprime_test_api.clear_histories() + time.sleep(2.0) + + # Fetch updated time + result: ChData = fprime_test_api.assert_telemetry( + "CdhCore.cmdDisp.CommandsDispatched", timeout=3 + ) + + # Convert FPrime time to datetime + fp_time: TimeType = result.time + updated_time = datetime.fromtimestamp(fp_time.seconds, tz=timezone.utc) + + # Assert time has increased + assert updated_time > initial_time, ( + f"Time should increase. Initial: {initial_time}, Updated: {updated_time}" + ) + + +def test_03_time_not_set_event(fprime_test_api: IntegrationTestAPI): + """Test that a TimeNotSet event is emitted when setting time with invalid data""" + + # Clear histories + fprime_test_api.clear_histories() + + # Send command to set the time with invalid data + time_data = dict( + Year=0, + Month=12345, + Day=12345, + Hour=12345, + Minute=12345, + Second=12345, + ) + time_data_str = json.dumps(time_data) + fprime_test_api.send_command( + "ReferenceDeployment.rtcManager.TIME_SET", + [ + time_data_str, + ], + ) + + # Assert time not set event is emitted + fprime_test_api.assert_event( + "ReferenceDeployment.rtcManager.YearValidationFailed", timeout=2 + ) + + fprime_test_api.assert_event( + "ReferenceDeployment.rtcManager.MonthValidationFailed", timeout=2 + ) + + fprime_test_api.assert_event( + "ReferenceDeployment.rtcManager.DayValidationFailed", timeout=2 + ) + + fprime_test_api.assert_event( + "ReferenceDeployment.rtcManager.HourValidationFailed", timeout=2 + ) + + fprime_test_api.assert_event( + "ReferenceDeployment.rtcManager.MinuteValidationFailed", timeout=2 + ) + + fprime_test_api.assert_event( + "ReferenceDeployment.rtcManager.SecondValidationFailed", timeout=2 + ) + + fprime_test_api.assert_event("ReferenceDeployment.rtcManager.TimeNotSet", timeout=2) + + fprime_test_api.assert_event("CdhCore.cmdDisp.OpCodeDispatched", timeout=2) + + fprime_test_api.assert_event("CdhCore.cmdDisp.OpCodeError", timeout=2) diff --git a/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi b/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi index 7b998db7..e93b7426 100644 --- a/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi +++ b/boards/bronco_space/proves_flight_control_board_v5/proves_flight_control_board_v5.dtsi @@ -119,7 +119,7 @@ zephyr_udc0: &usbd { compatible = "microcrystal,rv3028"; reg = <0x52>; int-gpios = <&gpio0 27 GPIO_ACTIVE_HIGH>; - backup-switch-mode = "level"; + backup-switch-mode = "direct"; label = "RV3028"; }; ina219: ina219@40 {