use serde::{Deserialize, Serialize};
use crate::{NtpDuration, NtpLeapIndicator, PeerSnapshot, PollInterval, ReferenceId, SystemConfig};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub struct TimeSnapshot {
pub poll_interval: PollInterval,
pub precision: NtpDuration,
pub root_delay: NtpDuration,
pub root_dispersion: NtpDuration,
pub leap_indicator: NtpLeapIndicator,
pub accumulated_steps: NtpDuration,
}
impl Default for TimeSnapshot {
fn default() -> Self {
Self {
poll_interval: PollInterval::default(),
precision: NtpDuration::from_exponent(-18),
root_delay: NtpDuration::ZERO,
root_dispersion: NtpDuration::ZERO,
leap_indicator: NtpLeapIndicator::Unknown,
accumulated_steps: NtpDuration::ZERO,
}
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct SystemSnapshot {
pub stratum: u8,
pub reference_id: ReferenceId,
pub accumulated_steps_threshold: Option<NtpDuration>,
#[serde(flatten)]
pub time_snapshot: TimeSnapshot,
}
impl SystemSnapshot {
pub fn update_timedata(&mut self, timedata: TimeSnapshot, config: &SystemConfig) {
self.time_snapshot = timedata;
self.accumulated_steps_threshold = config.accumulated_threshold;
}
pub fn update_used_peers(&mut self, mut used_peers: impl Iterator<Item = PeerSnapshot>) {
if let Some(system_peer_snapshot) = used_peers.next() {
self.stratum = system_peer_snapshot.stratum.saturating_add(1);
self.reference_id = system_peer_snapshot.reference_id;
}
}
}
impl Default for SystemSnapshot {
fn default() -> Self {
Self {
stratum: 16,
reference_id: ReferenceId::NONE,
accumulated_steps_threshold: None,
time_snapshot: TimeSnapshot::default(),
}
}
}
#[cfg(test)]
mod tests {
use crate::PollIntervalLimits;
use super::*;
#[test]
fn test_empty_peer_update() {
let mut system = SystemSnapshot::default();
system.update_used_peers(std::iter::empty());
assert_eq!(system.stratum, 16);
assert_eq!(system.reference_id, ReferenceId::NONE);
}
#[test]
fn test_peer_update() {
let mut system = SystemSnapshot::default();
system.update_used_peers(
vec![
PeerSnapshot {
peer_id: ReferenceId::KISS_DENY,
our_id: ReferenceId::NONE,
poll_interval: PollIntervalLimits::default().max,
reach: Default::default(),
stratum: 2,
reference_id: ReferenceId::KISS_DENY,
},
PeerSnapshot {
peer_id: ReferenceId::KISS_RATE,
our_id: ReferenceId::KISS_RSTR,
poll_interval: PollIntervalLimits::default().max,
reach: Default::default(),
stratum: 3,
reference_id: ReferenceId::NONE,
},
]
.into_iter(),
);
assert_eq!(system.stratum, 3);
assert_eq!(system.reference_id, ReferenceId::KISS_DENY);
}
#[test]
fn test_timedata_update() {
let mut system = SystemSnapshot::default();
let new_root_delay = NtpDuration::from_seconds(1.0);
let new_accumulated_threshold = NtpDuration::from_seconds(2.0);
let snapshot = TimeSnapshot {
root_delay: new_root_delay,
..Default::default()
};
system.update_timedata(
snapshot,
&SystemConfig {
accumulated_threshold: Some(new_accumulated_threshold),
..Default::default()
},
);
assert_eq!(system.time_snapshot, snapshot);
assert_eq!(
system.accumulated_steps_threshold,
Some(new_accumulated_threshold),
);
}
}