use std::{
convert::TryFrom,
ops::{Add, Sub},
time::Duration,
};
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use log::error;
use rustdds::Timestamp;
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)]
pub struct ROSTime {
nanos_since_epoch: i64,
}
impl ROSTime {
pub(crate) fn now() -> Self {
Self::try_from(chrono::Utc::now()).unwrap_or(Self::ZERO)
}
pub const ZERO: Self = Self::from_nanos(0);
pub const UNIX_EPOCH: Self = Self::from_nanos(0);
pub fn to_nanos(&self) -> i64 {
self.nanos_since_epoch
}
pub const fn from_nanos(nanos_since_unix_epoch: i64) -> Self {
Self {
nanos_since_epoch: nanos_since_unix_epoch,
}
}
}
#[derive(Clone, Debug)]
pub struct OutOfRangeError {}
impl TryFrom<chrono::DateTime<Utc>> for ROSTime {
type Error = OutOfRangeError;
fn try_from(chrono_time: chrono::DateTime<Utc>) -> Result<ROSTime, OutOfRangeError> {
chrono_time
.timestamp_nanos_opt()
.ok_or_else(|| {
error!(
"ROSTime: chrono timestamp is out of range: {:?}",
chrono_time
);
OutOfRangeError {}
})
.map(ROSTime::from_nanos)
}
}
impl From<ROSTime> for chrono::DateTime<Utc> {
fn from(rt: ROSTime) -> chrono::DateTime<Utc> {
DateTime::<Utc>::from_timestamp_nanos(rt.to_nanos())
}
}
impl From<ROSTime> for Timestamp {
fn from(rt: ROSTime) -> Timestamp {
let chrono_time = chrono::DateTime::<Utc>::from(rt);
Timestamp::try_from(chrono_time).unwrap_or_else(|e| {
error!(
"Time conversion ROSTime to Timestamp error: {} source={:?}",
e, rt
);
rustdds::Timestamp::INVALID
})
}
}
pub enum TimestampConversionError {
Invalid,
Infinite,
}
impl TryFrom<Timestamp> for ROSTime {
type Error = TimestampConversionError;
fn try_from(ts: Timestamp) -> Result<ROSTime, TimestampConversionError> {
match ts {
Timestamp::INVALID => Err(TimestampConversionError::Invalid),
Timestamp::INFINITE => Err(TimestampConversionError::Infinite),
ts => {
let ticks: u64 = ts.to_ticks(); let seconds = ticks >> 32;
let frac_ticks = ticks - (seconds << 32); let frac_nanos = (frac_ticks * 1_000_000_000) >> 32;
Ok(ROSTime::from_nanos(
(seconds * 1_000_000_000 + frac_nanos) as i64,
))
}
}
}
}
impl Sub for ROSTime {
type Output = ROSDuration;
fn sub(self, other: ROSTime) -> ROSDuration {
ROSDuration {
diff: self.nanos_since_epoch - other.nanos_since_epoch,
}
}
}
impl Sub<ROSDuration> for ROSTime {
type Output = ROSTime;
fn sub(self, other: ROSDuration) -> ROSTime {
ROSTime {
nanos_since_epoch: self.nanos_since_epoch - other.diff,
}
}
}
impl Add<ROSDuration> for ROSTime {
type Output = ROSTime;
fn add(self, other: ROSDuration) -> ROSTime {
ROSTime {
nanos_since_epoch: self.nanos_since_epoch + other.diff,
}
}
}
pub struct ROSDuration {
diff: i64,
}
impl ROSDuration {
pub const fn from_nanos(nanos: i64) -> Self {
ROSDuration { diff: nanos }
}
pub const fn to_nanos(&self) -> i64 {
self.diff
}
}
impl TryFrom<Duration> for ROSDuration {
type Error = OutOfRangeError;
fn try_from(std_duration: Duration) -> Result<Self, Self::Error> {
let nanos = std_duration.as_nanos();
if nanos <= (i64::MAX as u128) {
Ok(ROSDuration { diff: nanos as i64 })
} else {
Err(OutOfRangeError {})
}
}
}
impl TryFrom<ROSDuration> for Duration {
type Error = OutOfRangeError;
fn try_from(ros_duration: ROSDuration) -> Result<Duration, Self::Error> {
Ok(Duration::from_nanos(
u64::try_from(ros_duration.to_nanos()).map_err(|_e| OutOfRangeError {})?,
))
}
}
impl From<ROSDuration> for chrono::Duration {
fn from(d: ROSDuration) -> chrono::Duration {
chrono::Duration::nanoseconds(d.to_nanos())
}
}
impl TryFrom<chrono::Duration> for ROSDuration {
type Error = OutOfRangeError;
fn try_from(c_duration: chrono::Duration) -> Result<Self, Self::Error> {
c_duration
.num_nanoseconds()
.map(ROSDuration::from_nanos)
.ok_or(OutOfRangeError {})
}
}
impl Add for ROSDuration {
type Output = ROSDuration;
fn add(self, other: ROSDuration) -> ROSDuration {
ROSDuration {
diff: self.diff + other.diff,
}
}
}
impl Sub for ROSDuration {
type Output = ROSDuration;
fn sub(self, other: ROSDuration) -> ROSDuration {
ROSDuration {
diff: self.diff - other.diff,
}
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Serialize, Deserialize)]
pub struct SystemTime {
ros_time: ROSTime,
}
#[cfg(test)]
mod test {
#[test]
fn conversion() {
}
}