1#![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
13const DEFAULT_DUTY_CYCLE: u8 = 50;
15
16const DEFAULT_RPM: u16 = 500;
18
19const 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 fan_list: [VirtualFan; N],
56
57 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}