Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OsTime component #3355

Open
wants to merge 15 commits into
base: devel
Choose a base branch
from
Open

Add OsTime component #3355

wants to merge 15 commits into from

Conversation

kubiak-jpl
Copy link
Contributor

@kubiak-jpl kubiak-jpl commented Mar 10, 2025

Related Issue(s) #3335
Has Unit Tests (y/n) y
Documentation Included (y/n) y

Change Description

Creates a Time provides that uses the underlying OSAL RawTime interfaces to generate timestamps.

The RawTime layer only provides time intervals so the OsTime class needs to be passed a pair of epoch times, an Fw::Time and Os::RawTime object that correspond to the same moment in time. OsTime calculates the offset from the epoch RawTime as an Fw::TimeInterval, then adds this interval to the epoch Fw::Time object to calculate the final Fw::Time object.

Rationale

Provide a generic Time provider that can use any RawTime implementation. Original rationale is for microcontroller platforms which cannot use the existing PosixTime or ChronoTime components.

Testing/Review Recommendations

Provided unit tests are fairly barebones and just show the general behavior. It is expected that the underlying RawTime implementations will be more throughly tested.

  • How should overflow of the Fw::Time be handled, if at all?

Future Work

Possible extensions:

  • EVR on RawTime errors. These are not implemented because EVRs themselves require a Time call.

kubiak-jpl and others added 4 commits March 5, 2025 16:23

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
|-----------------|--------------------------------------------------------------------------------------------------------------------|--------------|
| SVC-OS-TIME-001 | `Svc::OsTime` shall return current system time as an `Fw::Time` objects in response to the `timeGetPort` port call | Unit Test |
| SVC-OS-TIME-002 | `Svc::OsTime` shall return ZERO_TIME as an `Fw::Time` object if the RawTime layer returns an error code | Unit Test |

Copy link
Collaborator

Choose a reason for hiding this comment

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

Would you add a section for configuration or setup or usage? I get what you are trying to do, but I a write-up of what I need to call in the topology to set this up would be nice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. Is there an existing component with a documentation structure you'd prefer I'd use here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the sdd in the latest comment

@LeStarch LeStarch requested a review from thomas-bc March 12, 2025 00:38
@LeStarch
Copy link
Collaborator

@thomas-bc would you be willing to review too? You are the author of RawTime.

OsTime ::~OsTime() {}

void OsTime::set_epoch(const Fw::Time& fw_time, const Os::RawTime& os_time) {
m_epoch_fw_time = fw_time;

Check warning

Code scanning / CodeQL

Unchecked return value Warning

The return value of non-void function
operator=
is not checked.

void OsTime::set_epoch(const Fw::Time& fw_time, const Os::RawTime& os_time) {
m_epoch_fw_time = fw_time;
m_epoch_os_time = os_time;

Check warning

Code scanning / CodeQL

Unchecked return value Warning

The return value of non-void function
operator=
is not checked.
// ----------------------------------------------------------------------

void OsTime ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) {
time = Fw::ZERO_TIME;

Check warning

Code scanning / CodeQL

Unchecked return value Warning

The return value of non-void function
operator=
is not checked.
@kubiak-jpl
Copy link
Contributor Author

@thomas-bc I noticed the MacOS unit test is failing, probably because the UTs rely on sleeping and time checks. I though 100 ms was generous enough, but I guess not. Is there a recommended way to stub out the OSAL layer for components UTs like this?

@thomas-bc
Copy link
Collaborator

thomas-bc commented Mar 13, 2025

@kubiak-jpl I would have thought 100ms be more than enough too... Running your tests on an M4 macOS the tests start failing when approaching 15-10ms threshold, but the GitHub VMs are busy bees... also not sure how much more precision we can expect from std::sleep_for + wake up time

I'm going to re-run your tests in CI once and see if that was transient or if we need to rethink a little. What are you thinking of stubbing the OSAL layer for here?

OsTime ::~OsTime() {}

void OsTime::set_epoch(const Fw::Time& fw_time, const Os::RawTime& os_time) {
m_epoch_fw_time = fw_time;
Copy link
Collaborator

@thomas-bc thomas-bc Mar 13, 2025

Choose a reason for hiding this comment

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

Suggested change
m_epoch_fw_time = fw_time;
(void) m_epoch_fw_time = fw_time;

To appease CI warnings, and in the other uses of time_x = time_y below

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That approach gives me compiler errors on my machine

[ 77%] Building CXX object F-Prime/Svc/OsTime/CMakeFiles/Svc_OsTime.dir/OsTime.cpp.o
/home/kubiak/source/fprime/fprime-kubiak/Svc/OsTime/OsTime.cpp: In member function ‘void Svc::OsTime::set_epoch(const Fw::Time&, const Os::RawTime&)’:
/home/kubiak/source/fprime/fprime-kubiak/Svc/OsTime/OsTime.cpp:31:30: error: invalid use of ‘void’
   31 |     (void) m_epoch_fw_time = fw_time;
      |                              ^~~~~~~
/home/kubiak/source/fprime/fprime-kubiak/Svc/OsTime/OsTime.cpp:32:30: error: invalid use of ‘void’
   32 |     (void) m_epoch_os_time = os_time;
      |                              ^~~~~~~
/home/kubiak/source/fprime/fprime-kubiak/Svc/OsTime/OsTime.cpp:33:28: error: invalid use of ‘void’
   33 |     (void) m_epoch_valid = true;
      |                            ^~~~
/home/kubiak/source/fprime/fprime-kubiak/Svc/OsTime/OsTime.cpp: In member function ‘virtual void Svc::OsTime::timeGetPort_handler(FwIndexType, Fw::Time&)’:
/home/kubiak/source/fprime/fprime-kubiak/Svc/OsTime/OsTime.cpp:60:19: error: invalid use of ‘void’
   60 |     (void) time = m_epoch_fw_time;

Copy link
Collaborator

Choose a reason for hiding this comment

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

My bad sorry, looks like the syntax should be the following for return values of the assignment operator

    (void) (m_epoch_fw_time = fw_time);

That's pretty ugly 😮‍💨

m_epoch_valid = true; probably doesn't need a cast

Copy link
Collaborator

Choose a reason for hiding this comment

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

We can also just dismiss the warning

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'd prefer to dismiss the warnings. I'm not really sure what it even means to check the return type of an assignment outside of using the assigned variable

ASSERT_NE(time_600ms, Fw::ZERO_TIME);

const double time_600 = time_600ms.getSeconds() + (time_600ms.getUSeconds() / (1000.*1000.));
ASSERT_NEAR(0.6, time_600, 0.1);
Copy link
Collaborator

Choose a reason for hiding this comment

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

For the tests that are causing issues, maybe rather than ASSERT_NEAR, you could e.g.

ASSERT_GT(time_600, 0.6) // should be deterministically true
ASSERT_LT(time_600, 0.6 * 1.5) // give some extra time to the slow github runners

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried something similar to this. Let's see if it passes CI.

I think the ideal solution would be for a set a OSAL implementations that are designed for unit testing. Like a UtRawTime implementation that I could pass Fw::Time structures to for unit testing, but that would probably be a bigger discussion outside of the PR

@kubiak-jpl
Copy link
Contributor Author

@LeStarch @thomas-bc

I made a few more substantial changes since the initial commit

  • I added a setEpoch port call so that another component could set the new epoch time. I'm thinking of a time sync command handler here
  • Added an Os::Mutex and a ScopeLock to protect the epoch variables

kubiak-jpl and others added 2 commits March 13, 2025 12:27
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------

void OsTime ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) {

Check notice

Code scanning / CodeQL

Long function without assertion Note

All functions of more than 10 lines should have at least one assertion.
return;
}

time = m_epoch_fw_time;

Check warning

Code scanning / CodeQL

Unchecked return value Warning

The return value of non-void function
operator=
is not checked.
// Handler implementations for user-defined typed input ports
// ----------------------------------------------------------------------

void OsTime ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) {

Check notice

Code scanning / CodeQL

Use of basic integral type Note

portNum uses the basic integral type int rather than a typedef with size and signedness.
time.add(elapsed.getSeconds(), elapsed.getUSeconds());
}

void OsTime ::setEpoch_handler(FwIndexType portNum, const Fw::Time& fw_time, const Os::RawTime& os_time) {

Check notice

Code scanning / CodeQL

Use of basic integral type Note

portNum uses the basic integral type int rather than a typedef with size and signedness.

Fw::Time m_epoch_fw_time;
Os::RawTime m_epoch_os_time;
bool m_epoch_valid;

Check notice

Code scanning / CodeQL

Use of basic integral type Note

m_epoch_valid uses the basic integral type bool rather than a typedef with size and signedness.
Copy link
Collaborator

@LeStarch LeStarch left a comment

Choose a reason for hiding this comment

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

Need to fix UTs on mac. It would be best to define the port outside of the component as that keeps components from having module-link dependencies on one-another.


@ A pair of timestamps representing an Epoch time in
@ an Fw::Time and Os::RawTime object
port OsTimeEpoch(
Copy link
Collaborator

Choose a reason for hiding this comment

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

For module independence, ports should typically be defined outside of the component.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. Where would you like me to add it?

Copy link
Collaborator

Choose a reason for hiding this comment

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

There's an Svc/Ports/ module that this would likely fall under nicely.


void OsTime::set_epoch(const Fw::Time& fw_time, const Os::RawTime& os_time) {
Os::ScopeLock lock(m_epoch_lock);
m_epoch_fw_time = fw_time;

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter fw_time has not been checked.
void OsTime::set_epoch(const Fw::Time& fw_time, const Os::RawTime& os_time) {
Os::ScopeLock lock(m_epoch_lock);
m_epoch_fw_time = fw_time;
m_epoch_os_time = os_time;

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter os_time has not been checked.
void OsTime ::timeGetPort_handler(FwIndexType portNum, Fw::Time& time) {
Os::ScopeLock lock(m_epoch_lock);

time = Fw::ZERO_TIME;

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter time has not been checked.
}

void OsTime ::setEpoch_handler(FwIndexType portNum, const Fw::Time& fw_time, const Os::RawTime& os_time) {
set_epoch(fw_time, os_time);

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter fw_time has not been checked.
}

void OsTime ::setEpoch_handler(FwIndexType portNum, const Fw::Time& fw_time, const Os::RawTime& os_time) {
set_epoch(fw_time, os_time);

Check warning

Code scanning / CodeQL

Unchecked function argument Warning

This use of parameter os_time has not been checked.
@kubiak-jpl
Copy link
Contributor Author

@thomas-bc @LeStarch I reworked the unit tests to remove timing dependencies by adding a RawTimeTester module under the OsTime test directory. I suspect this could be more broadly useful though

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants