Skip to content

Commit

Permalink
Add steady time type.
Browse files Browse the repository at this point in the history
  • Loading branch information
jhelovuo committed Mar 15, 2024
1 parent db85ada commit a0d8c8e
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 1 deletion.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ clap = "4.4.3" # for msggen
itertools = "0.11.0" # for msggen
bstr = "1.6.2"
widestring = "1.0" # msggen
libc = "0.2.153"

[dev-dependencies]
log = "0.4"
Expand All @@ -58,4 +59,4 @@ ctrlc = "3.1.6"

# async examples
smol = "1.3"
async-io = "2.2.0"
async-io = "2.2.0"
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ pub mod parameters;
#[doc(hidden)]
pub mod pubsub;
pub mod service;
pub mod steady_time;
mod wide_string;

#[doc(hidden)]
Expand Down
167 changes: 167 additions & 0 deletions src/steady_time.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// Steady time has a arbitrary origin, but is guaranteed to run
// monotonically, regardless of clock corrections or timezones.
//
// Steady time should be used onyl when it is necessary, e.g. because of interacting with hardware.
// Steady time cannot be simulated. Use ROS time instead, whenever possible.
//

use std::fmt;
use std::ops::{Add, Sub};
use std::convert::TryFrom;
use std::time::Duration;
use serde::{Serialize,Deserialize};

use chrono::{DateTime,Utc};
use libc;

// Monotonic time in nanoseconds
//H
// To get offset to UTC time, use now_with_utc() note that this will change over time, latest
// at the next leap second.
#[derive(Clone,Copy,PartialEq,PartialOrd,Eq,Ord, Debug, Serialize, Deserialize)]
pub struct Time {
nano_counter : i64, // free-running nanosecond counter
}

impl Time {
pub fn now() -> Time {
let mut ts = libc::timespec { tv_sec: 0 , tv_nsec: 0, };
unsafe {
assert_eq!(0, libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts));
}
Time{ nano_counter: (ts.tv_sec as i64) * (10i64.pow(9)) + (ts.tv_nsec as i64) }
}

pub fn now_with_utc() -> (Time,DateTime<Utc>) {
let m0 = Self::now();
let utc = Utc::now();
let m1 = Self::now();
let diff = m1 - m0;
//println!("now_with_utc() diff = {} ns" , diff.as_nanos() );
// TODO: check that diff is very small and complain if not

// We add half of the diff to compensate for the difference in call times.
( m0 + TimeDiff::from_nanos( diff.as_nanos() / 2)
, utc
)
}

pub fn as_i64(self) -> i64 {
self.nano_counter
}

pub fn from_i64(nanos :i64) -> Time {
Time{ nano_counter: nanos }
}

// Time cannot implement the Zero trait, because Time does not implement Add (with itself).
// So we just implement a zero() function.
pub fn zero() -> Time
{
Time { nano_counter: 0 }
}
}

impl fmt::Display for Time {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
// TODO: needs a display customization
fmt::Debug::fmt(self, fmt)
}
}

impl Sub for Time {
type Output = TimeDiff;

fn sub(self, other: Time) -> TimeDiff {
TimeDiff::from_nanos( self.nano_counter - other.nano_counter )
}
}

impl Sub<TimeDiff> for Time {
type Output = Time;

fn sub(self, other: TimeDiff) -> Time {
Time{ nano_counter: self.nano_counter - other.as_nanos() }
}
}

impl Add<TimeDiff> for Time {
type Output = Time;

fn add(self, other: TimeDiff) -> Time {
Time{ nano_counter: self.nano_counter + other.as_nanos() }
}
}

// time difference as nanoseconds, can be negative, unlike std::time::Duration
#[derive(Clone,Copy,PartialEq,PartialOrd,Eq,Ord, Debug, Serialize, Deserialize)]
pub struct TimeDiff {
nano_diff : i64,
}

impl TimeDiff {
pub fn as_i64(self) -> i64 {
self.nano_diff
}

pub fn from_i64(nanos :i64) -> TimeDiff {
TimeDiff{ nano_diff: nanos }
}

pub const fn from_nanos(nanos : i64) -> TimeDiff {
TimeDiff { nano_diff : nanos }
}

pub const fn from_millis(millis : i64) -> TimeDiff {
TimeDiff { nano_diff : millis * 1_000_000 }
}

pub const fn from_secs(secs : i64) -> TimeDiff {
TimeDiff { nano_diff : secs * 1_000_000_000 }
}

pub const fn as_nanos(self) -> i64 {
self.nano_diff
}

pub const fn as_millis(self) -> i64 {
self.nano_diff / 1_000_000
}

#[allow(dead_code)]
pub const fn as_seconds(self) -> i64 {
self.nano_diff / 1_000_000_000
}

pub /*const*/ fn as_duration(self) -> Result<Duration, <u64 as TryFrom<i64>>::Error> {
u64::try_from(self.nano_diff).map( |n| Duration::from_nanos(n) )
}

pub /*const*/ fn as_saturating_duration(self) -> Duration {
self.as_duration().unwrap_or( Duration::from_secs(0) )
}

}

impl fmt::Display for TimeDiff {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
// TODO: needs a display customization
fmt::Debug::fmt(self, fmt)
}
}

impl Add for TimeDiff {
type Output = TimeDiff;
fn add(self,other: TimeDiff) -> TimeDiff {
TimeDiff{ nano_diff: self.nano_diff + other.nano_diff }
}
}



impl Sub for TimeDiff {
type Output = TimeDiff;
fn sub(self,other: TimeDiff) -> TimeDiff {
TimeDiff{ nano_diff: self.nano_diff - other.nano_diff }
}
}

0 comments on commit a0d8c8e

Please sign in to comment.