#![no_std]
#[cfg(any(test, feature = "std"))]
extern crate std;
#[cfg(any(test, feature = "alloc"))]
extern crate alloc;
use fans::{FanControl, FanSelect};
use thiserror::Error;
const DEFAULT_DUTY_CYCLE: u8 = 50;
const DEFAULT_RPM: u16 = 500;
const DEFAULT_RPM_MAXIMUM: u16 = 2_000;
#[derive(Clone, Copy, Debug, Error)]
pub enum Error {
#[error("Duty cycle out of range")]
DutyCycleOutOfRange,
#[error("Invalid fan selection")]
InvalidFan,
#[error("RPM out of range")]
RpmOutOfRange,
}
#[derive(Clone, Copy, Debug)]
struct VirtualFan {
duty_cycle: u8,
rpm: u16,
mode: FanControl,
}
impl Default for VirtualFan {
fn default() -> Self {
Self {
duty_cycle: DEFAULT_DUTY_CYCLE,
rpm: DEFAULT_RPM,
mode: FanControl::DutyCycle(DEFAULT_DUTY_CYCLE),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct VirtualFanDriver<const N: usize> {
fan_list: [VirtualFan; N],
rpm_maximum: u16,
}
impl<const N: usize> Default for VirtualFanDriver<N> {
fn default() -> Self {
Self {
fan_list: [VirtualFan::default(); N],
rpm_maximum: DEFAULT_RPM_MAXIMUM,
}
}
}
impl<const N: usize> VirtualFanDriver<N> {
pub fn count(&self) -> u8 {
N as u8
}
fn valid_fan(&self, select: FanSelect) -> Result<(), Error> {
if select.0 <= self.fan_list.len() as u8 && select.0 != 0 {
Ok(())
} else {
Err(Error::InvalidFan)
}
}
pub fn duty_cycle(&mut self, select: FanSelect) -> Result<u8, Error> {
self.valid_fan(select)?;
Ok(self.fan_list[select.0 as usize - 1].duty_cycle)
}
fn set_duty_cycle(&mut self, select: FanSelect, duty_cycle: u8) -> Result<(), Error> {
self.valid_fan(select)?;
if duty_cycle > 100 {
return Err(Error::DutyCycleOutOfRange);
}
self.fan_list[select.0 as usize - 1].duty_cycle = duty_cycle;
Ok(())
}
pub fn rpm(&mut self, select: FanSelect) -> Result<u16, Error> {
self.valid_fan(select)?;
Ok(self.fan_list[select.0 as usize - 1].rpm)
}
fn set_rpm(&mut self, select: FanSelect, rpm: u16) -> Result<(), Error> {
self.valid_fan(select)?;
if rpm > self.rpm_maximum {
return Err(Error::DutyCycleOutOfRange);
}
self.fan_list[select.0 as usize - 1].rpm = rpm;
Ok(())
}
pub fn report(&mut self, select: FanSelect) -> Result<(u8, u16), Error> {
self.valid_fan(select)?;
Ok((
self.fan_list[select.0 as usize - 1].duty_cycle,
self.fan_list[select.0 as usize - 1].rpm,
))
}
pub fn mode(&mut self, select: FanSelect) -> Result<FanControl, Error> {
self.valid_fan(select)?;
Ok(self.fan_list[select.0 as usize - 1].mode)
}
pub fn set_mode(&mut self, select: FanSelect, mode: FanControl) -> Result<(), Error> {
self.valid_fan(select)?;
match mode {
FanControl::DutyCycle(duty_cycle) => self.set_duty_cycle(select, duty_cycle)?,
FanControl::Rpm(rpm) => self.set_rpm(select, rpm)?,
}
self.fan_list[select.0 as usize - 1].mode = mode;
Ok(())
}
pub fn maximum_rpm(&mut self) -> u16 {
self.rpm_maximum
}
pub fn set_maximum_rpm(&mut self, rpm: u16) {
self.rpm_maximum = rpm;
}
#[cfg(feature = "log")]
pub fn dump_info(&self) -> Result<(), Error> {
use log;
for (i, fan) in self.fan_list.iter().enumerate() {
log::info!("Fan {}: {:?}", i + 1, fan);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_fan() {
let quad_driver: VirtualFanDriver<4> = VirtualFanDriver::default();
let select = FanSelect(0);
assert!(quad_driver.valid_fan(select).is_err());
for i in 1..=4 {
let select = FanSelect(i);
assert!(quad_driver.valid_fan(select).is_ok());
}
let select = FanSelect(5);
assert!(quad_driver.valid_fan(select).is_err());
}
#[test]
fn duty_cycle() {
let mut quad_driver: VirtualFanDriver<4> = VirtualFanDriver::default();
for duty in 0..=100_u8 {
for fan in 1..=4 {
let select = FanSelect(fan);
quad_driver.set_duty_cycle(select, duty).unwrap();
assert_eq!(quad_driver.duty_cycle(select).unwrap(), duty);
}
}
assert!(quad_driver.set_duty_cycle(FanSelect(1), 101).is_err());
}
#[test]
fn rpm() {
let mut quad_driver: VirtualFanDriver<4> = VirtualFanDriver::default();
for rpm in 0..=quad_driver.rpm_maximum {
for fan in 1..=4 {
let select = FanSelect(fan);
quad_driver.set_rpm(select, rpm).unwrap();
assert_eq!(quad_driver.rpm(select).unwrap(), rpm);
}
}
assert!(quad_driver
.set_rpm(FanSelect(1), DEFAULT_RPM_MAXIMUM + 1)
.is_err());
}
#[test]
fn maximum_rpm() {
let mut quad_driver: VirtualFanDriver<4> = VirtualFanDriver::default();
assert_eq!(quad_driver.maximum_rpm(), DEFAULT_RPM_MAXIMUM);
quad_driver.set_maximum_rpm(2_500);
assert_eq!(quad_driver.maximum_rpm(), 2_500);
}
#[test]
fn mode() {
let mut quad_driver: VirtualFanDriver<4> = VirtualFanDriver::default();
for fan in 1..=4 {
let select = FanSelect(fan);
assert_eq!(
quad_driver.mode(select).unwrap(),
FanControl::DutyCycle(DEFAULT_DUTY_CYCLE)
);
}
assert!(quad_driver
.set_mode(FanSelect(1), FanControl::DutyCycle(101))
.is_err());
for fan in 1..=4 {
let select = FanSelect(fan);
quad_driver
.set_mode(select, FanControl::Rpm(DEFAULT_RPM))
.unwrap();
assert_eq!(
quad_driver.mode(select).unwrap(),
FanControl::Rpm(DEFAULT_RPM)
);
}
}
}