virtual_fan/
lib.rs

1//! Virtual fan device driver for development, testing, and simulation.
2#![no_std]
3
4#[cfg(any(test, feature = "std"))]
5extern crate std;
6
7#[cfg(any(test, feature = "alloc"))]
8extern crate alloc;
9
10use fans::{FanControl, FanDutyCycle, FanRpm, FanSelect};
11use thiserror::Error;
12
13/// Duty cycle which virtual fans are set to by default.
14const DEFAULT_DUTY_CYCLE: u8 = 50;
15
16/// RPM which virtual fans are set to by default.
17const DEFAULT_RPM: u16 = 500;
18
19/// Maximum RPM value that a virtual fan can reach by default.
20const DEFAULT_RPM_MAXIMUM: u16 = 2_000;
21
22#[derive(Clone, Copy, Debug, Error)]
23#[cfg_attr(feature = "defmt", derive(defmt::Format))]
24pub enum Error {
25    #[error("Duty cycle out of range")]
26    DutyCycleOutOfRange,
27
28    #[error("Invalid fan selection")]
29    InvalidFan,
30
31    #[error("RPM out of range")]
32    RpmOutOfRange,
33}
34
35#[derive(Clone, Copy, Debug)]
36struct VirtualFan {
37    duty_cycle: FanDutyCycle,
38    rpm: FanRpm,
39    mode: FanControl,
40}
41
42impl Default for VirtualFan {
43    fn default() -> Self {
44        Self {
45            duty_cycle: DEFAULT_DUTY_CYCLE,
46            rpm: DEFAULT_RPM,
47            mode: FanControl::DutyCycle(DEFAULT_DUTY_CYCLE),
48        }
49    }
50}
51
52#[derive(Clone, Copy, Debug)]
53pub struct VirtualFanDriver<const N: usize> {
54    /// List of virtual fans
55    fan_list: [VirtualFan; N],
56
57    /// Maximum RPM value that a virtual fan can reach
58    rpm_maximum: FanRpm,
59}
60
61impl<const N: usize> Default for VirtualFanDriver<N> {
62    fn default() -> Self {
63        Self {
64            fan_list: [VirtualFan::default(); N],
65            rpm_maximum: DEFAULT_RPM_MAXIMUM,
66        }
67    }
68}
69
70impl<const N: usize> VirtualFanDriver<N> {
71    pub fn count(&self) -> u16 {
72        N as u16
73    }
74
75    fn valid_fan(&self, select: FanSelect) -> Result<(), Error> {
76        if select.0 <= self.fan_list.len() as u16 && select.0 != 0 {
77            Ok(())
78        } else {
79            Err(Error::InvalidFan)
80        }
81    }
82
83    pub fn duty_cycle(&mut self, select: FanSelect) -> Result<FanDutyCycle, Error> {
84        self.valid_fan(select)?;
85        Ok(self.fan_list[select.0 as usize - 1].duty_cycle)
86    }
87
88    fn set_duty_cycle(&mut self, select: FanSelect, duty_cycle: FanDutyCycle) -> Result<(), Error> {
89        self.valid_fan(select)?;
90
91        if duty_cycle > 100 {
92            return Err(Error::DutyCycleOutOfRange);
93        }
94
95        self.fan_list[select.0 as usize - 1].duty_cycle = duty_cycle;
96        Ok(())
97    }
98
99    pub fn rpm(&mut self, select: FanSelect) -> Result<FanRpm, Error> {
100        self.valid_fan(select)?;
101        Ok(self.fan_list[select.0 as usize - 1].rpm)
102    }
103
104    fn set_rpm(&mut self, select: FanSelect, rpm: FanRpm) -> Result<(), Error> {
105        self.valid_fan(select)?;
106
107        if rpm > self.rpm_maximum {
108            return Err(Error::DutyCycleOutOfRange);
109        }
110
111        self.fan_list[select.0 as usize - 1].rpm = rpm;
112        Ok(())
113    }
114
115    pub fn report(&mut self, select: FanSelect) -> Result<(FanDutyCycle, FanRpm), Error> {
116        self.valid_fan(select)?;
117        Ok((
118            self.fan_list[select.0 as usize - 1].duty_cycle,
119            self.fan_list[select.0 as usize - 1].rpm,
120        ))
121    }
122
123    pub fn mode(&mut self, select: FanSelect) -> Result<FanControl, Error> {
124        self.valid_fan(select)?;
125        Ok(self.fan_list[select.0 as usize - 1].mode)
126    }
127
128    pub fn set_mode(&mut self, select: FanSelect, mode: FanControl) -> Result<(), Error> {
129        self.valid_fan(select)?;
130        match mode {
131            FanControl::DutyCycle(duty_cycle) => self.set_duty_cycle(select, duty_cycle)?,
132            FanControl::Rpm(rpm) => self.set_rpm(select, rpm)?,
133        }
134        self.fan_list[select.0 as usize - 1].mode = mode;
135
136        Ok(())
137    }
138
139    pub fn maximum_rpm(&mut self) -> FanRpm {
140        self.rpm_maximum
141    }
142
143    pub fn set_maximum_rpm(&mut self, rpm: FanRpm) {
144        self.rpm_maximum = rpm;
145    }
146
147    #[cfg(feature = "log")]
148    pub fn dump_info(&self) -> Result<(), Error> {
149        use log;
150        for (i, fan) in self.fan_list.iter().enumerate() {
151            log::info!("Fan {}: {:?}", i + 1, fan);
152        }
153
154        Ok(())
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn valid_fan() {
164        let quad_driver: VirtualFanDriver<4> = VirtualFanDriver::default();
165
166        let select = FanSelect(0);
167        assert!(quad_driver.valid_fan(select).is_err());
168
169        for i in 1..=4 {
170            let select = FanSelect(i);
171            assert!(quad_driver.valid_fan(select).is_ok());
172        }
173
174        let select = FanSelect(5);
175        assert!(quad_driver.valid_fan(select).is_err());
176    }
177
178    #[test]
179    fn duty_cycle() {
180        let mut quad_driver: VirtualFanDriver<4> = VirtualFanDriver::default();
181
182        for duty in 0..=100_u8 {
183            for fan in 1..=4 {
184                let select = FanSelect(fan);
185                quad_driver.set_duty_cycle(select, duty).unwrap();
186                assert_eq!(quad_driver.duty_cycle(select).unwrap(), duty);
187            }
188        }
189
190        assert!(quad_driver.set_duty_cycle(FanSelect(1), 101).is_err());
191    }
192
193    #[test]
194    fn rpm() {
195        let mut quad_driver: VirtualFanDriver<4> = VirtualFanDriver::default();
196
197        for rpm in 0..=quad_driver.rpm_maximum {
198            for fan in 1..=4 {
199                let select = FanSelect(fan);
200                quad_driver.set_rpm(select, rpm).unwrap();
201                assert_eq!(quad_driver.rpm(select).unwrap(), rpm);
202            }
203        }
204
205        assert!(quad_driver
206            .set_rpm(FanSelect(1), DEFAULT_RPM_MAXIMUM + 1)
207            .is_err());
208    }
209
210    #[test]
211    fn maximum_rpm() {
212        let mut quad_driver: VirtualFanDriver<4> = VirtualFanDriver::default();
213
214        assert_eq!(quad_driver.maximum_rpm(), DEFAULT_RPM_MAXIMUM);
215
216        quad_driver.set_maximum_rpm(2_500);
217        assert_eq!(quad_driver.maximum_rpm(), 2_500);
218    }
219
220    #[test]
221    fn mode() {
222        let mut quad_driver: VirtualFanDriver<4> = VirtualFanDriver::default();
223
224        for fan in 1..=4 {
225            let select = FanSelect(fan);
226            assert_eq!(
227                quad_driver.mode(select).unwrap(),
228                FanControl::DutyCycle(DEFAULT_DUTY_CYCLE)
229            );
230        }
231
232        assert!(quad_driver
233            .set_mode(FanSelect(1), FanControl::DutyCycle(101))
234            .is_err());
235
236        for fan in 1..=4 {
237            let select = FanSelect(fan);
238            quad_driver
239                .set_mode(select, FanControl::Rpm(DEFAULT_RPM))
240                .unwrap();
241            assert_eq!(
242                quad_driver.mode(select).unwrap(),
243                FanControl::Rpm(DEFAULT_RPM)
244            );
245        }
246    }
247}