Repository: nasa/fprime (language: python)

Issue:
Implement std::chrono Based TimeInterface Implementation
| | |
|:---|:---|
|**_F´ Version_**| |
|**_Affected Component_**|  |
---
## Feature Description

`Svc::PosixTime` breaks on non-posix systems (e.g. VxWorks).  C++11 provides `std::chrono` for dealing with cross-platform time.  We should implement `Svc::ChronoTime` to use that library and provide an alternate to `Svc::PosixTime`


Discussion / hints:

Fix patch:
diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt
index 65b9766c64..8f1434d85f 100644
--- a/.github/actions/spelling/expect.txt
+++ b/.github/actions/spelling/expect.txt
@@ -952,6 +952,7 @@ Thhmmss
 thiscol
 thisdirdoesnotexist
 thisfiledoesnotexist
+Thu
 timebase
 timerfd
 timetag
diff --git a/Svc/CMakeLists.txt b/Svc/CMakeLists.txt
index 2ab95efc03..49de3f43aa 100644
--- a/Svc/CMakeLists.txt
+++ b/Svc/CMakeLists.txt
@@ -17,6 +17,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/BufferAccumulator/")
 add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/BufferManager/")
 add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/BufferLogger/")
 add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/BufferRepeater/")
+add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ChronoTime/")
 add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ComLogger/")
 add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ComQueue/")
 add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ComSplitter/")
@@ -58,4 +59,4 @@ endif()
 
 add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/PosixTime/")
 add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/LinuxTimer/")
-add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Version/")
\ No newline at end of file
+add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Version/")
diff --git a/Svc/ChronoTime/CMakeLists.txt b/Svc/ChronoTime/CMakeLists.txt
new file mode 100644
index 0000000000..01dff82fa1
--- /dev/null
+++ b/Svc/ChronoTime/CMakeLists.txt
@@ -0,0 +1,26 @@
+####
+# FPrime CMakeLists.txt:
+#
+# SOURCE_FILES: combined list of source and autocoding files
+# MOD_DEPS: (optional) module dependencies
+# UT_SOURCE_FILES: list of source files for unit tests
+#
+# More information in the F´ CMake API documentation:
+# https://nasa.github.io/fprime/UsersGuide/api/cmake/API.html
+#
+####
+
+set(SOURCE_FILES
+  "${CMAKE_CURRENT_LIST_DIR}/ChronoTime.fpp"
+  "${CMAKE_CURRENT_LIST_DIR}/ChronoTime.cpp"
+)
+
+register_fprime_module()
+
+set(UT_AUTO_HELPERS ON)
+set(UT_SOURCE_FILES
+        "${CMAKE_CURRENT_LIST_DIR}/ChronoTime.fpp"
+        "${CMAKE_CURRENT_LIST_DIR}/test/ut/ChronoTimeTester.cpp"
+        "${CMAKE_CURRENT_LIST_DIR}/test/ut/ChronoTimeTestMain.cpp"
+)
+register_fprime_ut()
\ No newline at end of file
diff --git a/Svc/ChronoTime/ChronoTime.cpp b/Svc/ChronoTime/ChronoTime.cpp
new file mode 100644
index 0000000000..4587125fd3
--- /dev/null
+++ b/Svc/ChronoTime/ChronoTime.cpp
@@ -0,0 +1,33 @@
+// ======================================================================
+// \title  ChronoTime.cpp
+// \author mstarch
+// \brief  cpp file for ChronoTime component implementation class
+// ======================================================================
+
+#include "Svc/ChronoTime/ChronoTime.hpp"
+#include <chrono>
+#include "FpConfig.hpp"
+
+namespace Svc {
+
+// ----------------------------------------------------------------------
+// Component construction and destruction
+// ----------------------------------------------------------------------
+
+ChronoTime ::ChronoTime(const char* const compName) : ChronoTimeComponentBase(compName) {}
+
+ChronoTime ::~ChronoTime() {}
+
+// ----------------------------------------------------------------------
+// Handler implementations for user-defined typed input ports
+// ----------------------------------------------------------------------
+
+void ChronoTime ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) {
+    const auto time_now = std::chrono::system_clock::now();
+    time.set(TimeBase::TB_WORKSTATION_TIME,
+             static_cast<U32>(std::chrono::duration_cast<std::chrono::seconds>(time_now.time_since_epoch()).count()),
+             static_cast<U32>(
+                 std::chrono::duration_cast<std::chrono::microseconds>(time_now.time_since_epoch()).count() % 1000000));
+}
+
+}  // namespace Svc
diff --git a/Svc/ChronoTime/ChronoTime.fpp b/Svc/ChronoTime/ChronoTime.fpp
new file mode 100644
index 0000000000..fccb91b780
--- /dev/null
+++ b/Svc/ChronoTime/ChronoTime.fpp
@@ -0,0 +1,6 @@
+module Svc {
+    @ A time component using C++11 chrono library
+    passive component ChronoTime {
+        include "../Interfaces/TimeInterface.fppi"
+    }
+}
\ No newline at end of file
diff --git a/Svc/ChronoTime/ChronoTime.hpp b/Svc/ChronoTime/ChronoTime.hpp
new file mode 100644
index 0000000000..bd0d904ab1
--- /dev/null
+++ b/Svc/ChronoTime/ChronoTime.hpp
@@ -0,0 +1,42 @@
+// ======================================================================
+// \title  ChronoTime.hpp
+// \author mstarch
+// \brief  hpp file for ChronoTime component implementation class
+// ======================================================================
+
+#ifndef Svc_ChronoTime_HPP
+#define Svc_ChronoTime_HPP
+
+#include "Svc/ChronoTime/ChronoTimeComponentAc.hpp"
+
+namespace Svc {
+
+class ChronoTime : public ChronoTimeComponentBase {
+  public:
+    // ----------------------------------------------------------------------
+    // Component construction and destruction
+    // ----------------------------------------------------------------------
+
+    //! Construct ChronoTime object
+    ChronoTime(const char* const compName  //!< The component name
+    );
+
+    //! Destroy ChronoTime object
+    ~ChronoTime();
+
+  PRIVATE:
+    // ----------------------------------------------------------------------
+    // Handler implementations for user-defined 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;
+};
+
+}  // namespace Svc
+
+#endif
diff --git a/Svc/ChronoTime/docs/sdd.md b/Svc/ChronoTime/docs/sdd.md
new file mode 100644
index 0000000000..0259732277
--- /dev/null
+++ b/Svc/ChronoTime/docs/sdd.md
@@ -0,0 +1,45 @@
+# Svc::ChronoTime
+
+As an F Prime project matures, a custom time component will likely be developed soliciting time in the project directed
+way. For reference projects and projects in an early phase of development, this custom time component does not exist.
+This miss-match was historically filled by Svc::PosixTime (formerly Svc::LinuxTime), however; this requires a project to
+support posix.
+
+The std::chrono library provided by C++11 is a language feature and as such can implement time without the need for
+posix.
+
+This is a C++11 implementation of the TimeInterface used to supply time to the various parts of the system.
+
+## Requirements
+
+The following requirements describe the behavior of Svc::ChronoTime.
+
+| Name                | Description                                                              | Validation |
+|---------------------|--------------------------------------------------------------------------|------------|
+| SVC_CHRONO_TIME_001 | Svc::ChronoTime shall be implemented using only C++11 language features. | Inspection |
+| SVC_CHRONO_TIME_002 | Svc::ChronoTime shall implement the Svc.TimeInterface.                   | Unit-Test  |
+| SVC_CHRONO_TIME_003 | Svc::ChronoTime shall provide time with resolution to microseconds.      | Unit-Test  |
+
+
+## Usage Examples
+
+In order to use this component, projects must supply `TimeBase::TB_WORKSTATION_TIME` in their `TimeBase` enumeration.
+
+To use Svc::ChronoTime in your project, make sure to set the time service with the following lines in your topology:
+
+**Add the instance to the instance list:**
+```
+    instance chronoTime: Svc.PosixTime base id 0x4500
+```
+
+**Use the instance to supply time:**
+```
+    time connections instance chronoTime
+```
+
+> Since an implementation of the time interface comes with he standard topology template, projects should replace it.
+
+## Change Log
+| Date       | Description   |
+|------------|---------------|
+| 2024-11-25 | Initial Draft |
\ No newline at end of file
diff --git a/Svc/LinuxTimer/CMakeLists.txt b/Svc/LinuxTimer/CMakeLists.txt
index 1a1547f8eb..aac80ceb6f 100644
--- a/Svc/LinuxTimer/CMakeLists.txt
+++ b/Svc/LinuxTimer/CMakeLists.txt
@@ -20,12 +20,6 @@ elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
 		"${CMAKE_CURRENT_LIST_DIR}/LinuxTimerComponentImplTimerFd.cpp"
 		"${CMAKE_CURRENT_LIST_DIR}/LinuxTimerComponentImplCommon.cpp"
 	)
-elseif(${CMAKE_SYSTEM_NAME} STREQUAL "RTEMS5")
-	set(SOURCE_FILES
-		"${CMAKE_CURRENT_LIST_DIR}/LinuxTimer.fpp"
-		"${CMAKE_CURRENT_LIST_DIR}/LinuxTimerComponentImplTaskDelay.cpp"
-		"${CMAKE_CURRENT_LIST_DIR}/LinuxTimerComponentImplCommon.cpp"
-		)
 else()
 	set(SOURCE_FILES
 		"${CMAKE_CURRENT_LIST_DIR}/LinuxTimer.fpp"
diff --git a/docs/UsersGuide/dev/os-docs.md b/docs/UsersGuide/dev/os-docs.md
index a61dcc80ff..ce4528cf97 100644
--- a/docs/UsersGuide/dev/os-docs.md
+++ b/docs/UsersGuide/dev/os-docs.md
@@ -5,7 +5,7 @@
 > This document is out of date and will be updated shortly.
 
 The framework incorporates an OSAL providing service abstraction classes of a fictitious operating system that can perform all the operations of a real operating system.
-This layer shows the user an abstraction of the common functionality provided by all real-time operating systems (RTEMS, VxWorks, Azure ThreadX, QNX, FreeRTOS, Zephyr,...) or not (Linux, macOS, WinCE,...). We find the following services:
+This layer shows the user an abstraction of the common functionality provided by all real-time operating systems (VxWorks, Azure ThreadX, QNX, FreeRTOS, Zephyr,...) or not (Linux, macOS, WinCE,...). We find the following services:
 
 1. multitasking management with prioritization ;
 2. synchronization ;
@@ -236,4 +236,4 @@ Table 30 provides the methods and their descriptions.
 | logMsg | Log a text message. The arguments are: |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
 |        | **Argument**                           | **Description**                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
 |        | fmt                                    | Format string to fill and print.                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
-|        | a1 to a6                               | Values to be printed. They will be inserted into the format string based on the format string specifiers. The type of the argument is POINTER\_CAST (normally an integer), but a typical usage is to cast the values to display to POINTER\_CAST and allow the format string extraction to get the correct value. This can include strings (char\*), but the location in memory that holds the string must persist since the format string may be printed on another task in the system. |
\ No newline at end of file
+|        | a1 to a6                               | Values to be printed. They will be inserted into the format string based on the format string specifiers. The type of the argument is POINTER\_CAST (normally an integer), but a typical usage is to cast the values to display to POINTER\_CAST and allow the format string extraction to get the correct value. This can include strings (char\*), but the location in memory that holds the string must persist since the format string may be printed on another task in the system. |
diff --git a/docs/UsersGuide/user/full-intro.md b/docs/UsersGuide/user/full-intro.md
index 823962bade..028aacc46c 100644
--- a/docs/UsersGuide/user/full-intro.md
+++ b/docs/UsersGuide/user/full-intro.md
@@ -88,5 +88,5 @@ required.  See: [F´ On Baremetal and Multi-Core Systems](../dev/baremetal-multi
 ## Conclusion
 
 The F′ software framework is released as open source and has been ported to Linux, macOS, Windows (WSL), VxWorks, ARINC
- 653, RTEMS, Baremetal(No OS), PPC, Leon3, x86, ARM (A15/A7), and MSP430. Mature sets of CD&H components are available
-following flight process, such as code inspections, static analysis, and full-coverage unit testing.
\ No newline at end of file
+ 653, Baremetal(No OS), PPC, Leon3, x86, ARM (A15/A7), and MSP430. Mature sets of CD&H components are available
+following flight process, such as code inspections, static analysis, and full-coverage unit testing.


Test patch:
diff --git a/Svc/ChronoTime/test/ut/ChronoTimeTestMain.cpp b/Svc/ChronoTime/test/ut/ChronoTimeTestMain.cpp
new file mode 100644
index 0000000000..0ed6f5e3cb
--- /dev/null
+++ b/Svc/ChronoTime/test/ut/ChronoTimeTestMain.cpp
@@ -0,0 +1,17 @@
+// ======================================================================
+// \title  ChronoTimeTestMain.cpp
+// \author mstarch
+// \brief  cpp file for ChronoTime component test main function
+// ======================================================================
+
+#include "ChronoTimeTester.hpp"
+
+TEST(Nominal, TestBasicTime) {
+    Svc::ChronoTimeTester tester;
+    tester.test_basic_time();
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/Svc/ChronoTime/test/ut/ChronoTimeTester.cpp b/Svc/ChronoTime/test/ut/ChronoTimeTester.cpp
new file mode 100644
index 0000000000..59de7aac7a
--- /dev/null
+++ b/Svc/ChronoTime/test/ut/ChronoTimeTester.cpp
@@ -0,0 +1,54 @@
+// ======================================================================
+// \title  ChronoTimeTester.cpp
+// \author mstarch
+// \brief  cpp file for ChronoTime component test harness implementation class
+// ======================================================================
+
+#include "ChronoTimeTester.hpp"
+#include <Fw/Test/UnitTest.hpp>
+#include <iostream>
+namespace Svc {
+
+// ----------------------------------------------------------------------
+// Construction and destruction
+// ----------------------------------------------------------------------
+
+ChronoTimeTester ::ChronoTimeTester()
+    : ChronoTimeGTestBase("ChronoTimeTester", ChronoTimeTester::MAX_HISTORY_SIZE), component("ChronoTime") {
+    this->initComponents();
+    this->connectPorts();
+}
+
+ChronoTimeTester ::~ChronoTimeTester() {}
+
+// ----------------------------------------------------------------------
+// Tests
+// ----------------------------------------------------------------------
+
+void ChronoTimeTester ::test_basic_time() {
+    const U32 SECONDS_SINCE_EPOCH_2024_11_25 = 1732492800ull; // Time since epoch as of date of writing
+    const std::string expected_epoch_time("Thu Jan  1 00:00:00 1970\n");  //std::asctime adds \n
+
+    // Calculate the system_clock epoch. This test is only valid when the system_clock uses unix epoch.
+    const auto epoch = std::chrono::time_point<std::chrono::system_clock>{};
+    std::time_t epoch_time = std::chrono::system_clock::to_time_t(epoch);
+    const std::string reported_epoch_time(std::asctime(std::gmtime(&epoch_time)));
+
+    // Skip test when epoch is not the unix epoch
+    if (expected_epoch_time != reported_epoch_time) {
+        GTEST_SKIP() << "Cannot run std::chrono test with non-unix epoch of: " << reported_epoch_time;
+    }
+
+    REQUIREMENT("SVC_CHRONO_TIME_002");
+    REQUIREMENT("SVC_CHRONO_TIME_003");
+
+    // Invoke port
+    Fw::Time time;
+    this->invoke_to_timeGetPort(0, time);
+    ASSERT_GT(time.getSeconds(), SECONDS_SINCE_EPOCH_2024_11_25);
+    // Check for correct use of milliseconds
+    ASSERT_GE(time.getUSeconds(), 0U);
+    ASSERT_LE(time.getUSeconds(), 999999U);
+}
+
+}  // namespace Svc
diff --git a/Svc/ChronoTime/test/ut/ChronoTimeTester.hpp b/Svc/ChronoTime/test/ut/ChronoTimeTester.hpp
new file mode 100644
index 0000000000..71f9565c5f
--- /dev/null
+++ b/Svc/ChronoTime/test/ut/ChronoTimeTester.hpp
@@ -0,0 +1,68 @@
+// ======================================================================
+// \title  ChronoTimeTester.hpp
+// \author mstarch
+// \brief  hpp file for ChronoTime component test harness implementation class
+// ======================================================================
+
+#ifndef Svc_ChronoTimeTester_HPP
+#define Svc_ChronoTimeTester_HPP
+
+#include "Svc/ChronoTime/ChronoTime.hpp"
+#include "Svc/ChronoTime/ChronoTimeGTestBase.hpp"
+
+namespace Svc {
+
+class ChronoTimeTester : public ChronoTimeGTestBase {
+  public:
+    // ----------------------------------------------------------------------
+    // Constants
+    // ----------------------------------------------------------------------
+
+    // Maximum size of histories storing events, telemetry, and port outputs
+    static const FwSizeType MAX_HISTORY_SIZE = 10;
+
+    // Instance ID supplied to the component instance under test
+    static const FwEnumStoreType TEST_INSTANCE_ID = 0;
+
+  public:
+    // ----------------------------------------------------------------------
+    // Construction and destruction
+    // ----------------------------------------------------------------------
+
+    //! Construct object ChronoTimeTester
+    ChronoTimeTester();
+
+    //! Destroy object ChronoTimeTester
+    ~ChronoTimeTester();
+
+  public:
+    // ----------------------------------------------------------------------
+    // Tests
+    // ----------------------------------------------------------------------
+
+    //! To do
+    void test_basic_time();
+
+  private:
+    // ----------------------------------------------------------------------
+    // Helper functions
+    // ----------------------------------------------------------------------
+
+    //! Connect ports
+    void connectPorts();
+
+    //! Initialize components
+    void initComponents();
+
+  private:
+    // ----------------------------------------------------------------------
+    // Member variables
+    // ----------------------------------------------------------------------
+
+    //! The component under test
+    ChronoTime component;
+};
+
+}  // namespace Svc
+
+#endif