use crate::core::scalar::ControlScalar;
#[derive(Debug, Clone)]
pub struct Watchdog<S: ControlScalar> {
timeout: S,
elapsed: S,
tripped: bool,
}
impl<S: ControlScalar> Watchdog<S> {
pub fn new(timeout: S) -> Self {
Self {
timeout,
elapsed: S::ZERO,
tripped: false,
}
}
pub fn kick(&mut self) {
self.elapsed = S::ZERO;
}
pub fn check(&mut self, dt: S) -> bool {
if self.tripped {
return true;
}
self.elapsed += dt;
if self.elapsed >= self.timeout {
self.tripped = true;
true
} else {
false
}
}
pub fn is_tripped(&self) -> bool {
self.tripped
}
pub fn reset(&mut self) {
self.elapsed = S::ZERO;
self.tripped = false;
}
pub fn timeout(&self) -> S {
self.timeout
}
pub fn elapsed(&self) -> S {
self.elapsed
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_watchdog_not_tripped() {
let wd = Watchdog::<f64>::new(1.0);
assert!(!wd.is_tripped());
}
#[test]
fn trips_after_timeout() {
let mut wd = Watchdog::<f64>::new(0.1);
assert!(!wd.check(0.05));
assert!(!wd.is_tripped());
assert!(wd.check(0.06));
assert!(wd.is_tripped());
}
#[test]
fn kick_resets_timer() {
let mut wd = Watchdog::<f64>::new(0.1);
wd.check(0.08);
assert!(!wd.is_tripped());
wd.kick();
assert!(!wd.check(0.08));
assert!(!wd.is_tripped());
}
#[test]
fn stays_tripped() {
let mut wd = Watchdog::<f64>::new(0.1);
wd.check(0.2);
assert!(wd.is_tripped());
wd.kick();
assert!(wd.is_tripped());
}
#[test]
fn reset_clears_trip() {
let mut wd = Watchdog::<f64>::new(0.1);
wd.check(0.2);
assert!(wd.is_tripped());
wd.reset();
assert!(!wd.is_tripped());
assert_eq!(wd.elapsed(), 0.0);
}
#[test]
fn f32_watchdog() {
let mut wd = Watchdog::<f32>::new(0.5);
for _ in 0..4 {
assert!(!wd.check(0.1));
}
assert!(wd.check(0.15));
assert!(wd.is_tripped());
}
}