autd3_core/ethercat/
dc_sys_time.rs

1use time::OffsetDateTime;
2
3use thiserror::Error;
4
5#[derive(Error, Debug, PartialEq, Clone)]
6#[error("Invalid date time")]
7pub struct InvalidDateTime;
8
9use super::ECAT_DC_SYS_TIME_BASE;
10
11/// The system time of the Distributed Clock
12///
13/// The system time is the time expressed in 1ns units with 2000-01-01 0:00:00 UTC as the reference.
14/// It is expressed as a 64-bit unsigned integer and can represent about 584 years of time.
15/// See [EtherCAT Distributed Clock](https://infosys.beckhoff.com/english.php?content=../content/1033/ethercatsystem/2469118347.html) for more information.
16#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[repr(C)]
18pub struct DcSysTime {
19    dc_sys_time: u64,
20}
21
22impl DcSysTime {
23    /// The zero point of the DcSysTime (2000-01-01 0:00:00 UTC)
24    pub const ZERO: Self = Self { dc_sys_time: 0 };
25
26    /// Returns the system time in nanoseconds
27    #[must_use]
28    pub const fn sys_time(&self) -> u64 {
29        self.dc_sys_time
30    }
31
32    /// Converts the system time to the UTC time
33    #[must_use]
34    pub fn to_utc(&self) -> OffsetDateTime {
35        ECAT_DC_SYS_TIME_BASE + std::time::Duration::from_nanos(self.dc_sys_time)
36    }
37
38    /// Creates a new instance from the UTC time
39    pub fn from_utc(utc: OffsetDateTime) -> Result<Self, InvalidDateTime> {
40        Ok(Self {
41            dc_sys_time: u64::try_from((utc - ECAT_DC_SYS_TIME_BASE).whole_nanoseconds())
42                .map_err(|_| InvalidDateTime)?,
43        })
44    }
45
46    /// Returns the system time of now
47    #[must_use]
48    pub fn now() -> Self {
49        Self::from_utc(OffsetDateTime::now_utc()).unwrap()
50    }
51}
52
53impl std::ops::Add<std::time::Duration> for DcSysTime {
54    type Output = Self;
55
56    fn add(self, rhs: std::time::Duration) -> Self::Output {
57        Self {
58            dc_sys_time: self.dc_sys_time + rhs.as_nanos() as u64,
59        }
60    }
61}
62
63impl std::ops::Sub<std::time::Duration> for DcSysTime {
64    type Output = Self;
65
66    fn sub(self, rhs: std::time::Duration) -> Self::Output {
67        Self {
68            dc_sys_time: self.dc_sys_time - rhs.as_nanos() as u64,
69        }
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn now_dc_sys_time() {
79        let t = DcSysTime::now();
80        assert!(t.sys_time() > 0);
81    }
82
83    #[rstest::rstest]
84    #[test]
85    #[case(Ok(DcSysTime { dc_sys_time: 0 }), time::macros::datetime!(2000-01-01 0:0:0 UTC))]
86    #[case(Ok(DcSysTime { dc_sys_time: 1000000000 }), time::macros::datetime!(2000-01-01 0:0:1 UTC))]
87    #[case(Ok(DcSysTime { dc_sys_time: 31622400000000000 }), time::macros::datetime!(2001-01-01 0:0:0 UTC))]
88    #[case(Err(InvalidDateTime), time::macros::datetime!(1999-01-01 0:0:1 UTC))]
89    #[case(Err(InvalidDateTime), time::macros::datetime!(9999-01-01 0:0:1 UTC))]
90    fn from_utc(#[case] expect: Result<DcSysTime, InvalidDateTime>, #[case] utc: OffsetDateTime) {
91        assert_eq!(expect, DcSysTime::from_utc(utc));
92    }
93
94    #[rstest::rstest]
95    #[test]
96    #[case(time::macros::datetime!(2000-01-01 0:0:1 UTC))]
97    #[case(time::macros::datetime!(2001-01-01 0:0:0 UTC))]
98    fn to_utc(#[case] utc: OffsetDateTime) {
99        assert_eq!(utc, DcSysTime::from_utc(utc).unwrap().to_utc());
100    }
101
102    #[test]
103    fn addsub() {
104        let utc = time::macros::datetime!(2000-01-01 0:0:0 UTC);
105        let t = DcSysTime::from_utc(utc);
106        assert!(t.is_ok());
107        let t = t.unwrap() + std::time::Duration::from_secs(1);
108        assert_eq!(1000000000, t.sys_time());
109
110        let t = t - std::time::Duration::from_secs(1);
111        assert_eq!(0, t.sys_time());
112    }
113}