#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Thermostat {
setpoint: f32,
hysteresis: f32,
cools: bool,
on: bool,
}
impl Thermostat {
pub fn cooling(setpoint: f32, hysteresis: f32) -> Self {
Self {
setpoint,
hysteresis: magnitude(hysteresis),
cools: true,
on: false,
}
}
pub fn heating(setpoint: f32, hysteresis: f32) -> Self {
Self {
setpoint,
hysteresis: magnitude(hysteresis),
cools: false,
on: false,
}
}
pub fn update(&mut self, reading: f32) -> bool {
let upper = self.setpoint + self.hysteresis;
let lower = self.setpoint - self.hysteresis;
if self.cools {
if reading >= upper {
self.on = true;
} else if reading <= lower {
self.on = false;
}
} else if reading <= lower {
self.on = true;
} else if reading >= upper {
self.on = false;
}
self.on
}
pub fn is_on(&self) -> bool {
self.on
}
}
fn magnitude(value: f32) -> f32 {
if value < 0.0 {
-value
} else {
value
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cooling_switches_around_the_deadband() {
let mut fridge = Thermostat::cooling(4.0, 0.5);
assert!(!fridge.is_on());
assert!(fridge.update(4.6)); assert!(fridge.update(4.2)); assert!(!fridge.update(3.4)); assert!(!fridge.update(4.2)); }
#[test]
fn heating_switches_the_other_way() {
let mut heater = Thermostat::heating(20.0, 1.0);
assert!(heater.update(18.5)); assert!(heater.update(19.5)); assert!(!heater.update(21.5)); }
#[test]
fn negative_hysteresis_is_treated_as_its_magnitude() {
let mut fridge = Thermostat::cooling(4.0, -0.5);
assert!(fridge.update(4.6));
assert!(!fridge.update(3.4));
}
}