use std::sync::atomic::{AtomicU64, Ordering};
use std::time::Duration;
#[derive(Debug)]
pub struct BusTiming {
last_event_us: AtomicU64,
min_spacing_us: u64,
}
impl BusTiming {
pub fn custom(min_spacing: Duration) -> Self {
let min_spacing_us = min_spacing.as_micros() as u64;
Self {
last_event_us: AtomicU64::new(0),
min_spacing_us,
}
}
pub fn rtu_35t(baud_rate: u32) -> Self {
let min_spacing_us = if baud_rate > 19200 {
1750 } else {
(3_500_000u64 * 11) / baud_rate.max(1) as u64
};
Self {
last_event_us: AtomicU64::new(0),
min_spacing_us,
}
}
pub fn rtu_35t_raw(baud_rate: u32) -> Self {
let min_spacing_us = (3_500_000u64 * 11) / baud_rate.max(1) as u64;
Self {
last_event_us: AtomicU64::new(0),
min_spacing_us,
}
}
pub fn ascii_35t(baud_rate: u32) -> Self {
Self::rtu_35t(baud_rate)
}
#[inline]
pub fn touch(&self) {
let now = now_micros().max(1);
self.last_event_us.store(now, Ordering::Relaxed);
}
pub async fn wait_if_needed(&self) {
let last = self.last_event_us.load(Ordering::Relaxed);
if last == 0 {
return; }
let now = now_micros();
let elapsed = now.saturating_sub(last);
if elapsed < self.min_spacing_us {
let remaining_us = self.min_spacing_us - elapsed;
tokio::time::sleep(Duration::from_micros(remaining_us)).await;
}
}
pub fn min_spacing(&self) -> Duration {
Duration::from_micros(self.min_spacing_us)
}
}
fn now_micros() -> u64 {
static BASE: std::sync::OnceLock<std::time::Instant> = std::sync::OnceLock::new();
let base = BASE.get_or_init(std::time::Instant::now);
base.elapsed().as_micros() as u64
}