scd30_interface/data/
forced_recalibration_value.rs

1use byteorder::{BigEndian, ByteOrder};
2
3use crate::error::DataError;
4use crate::util::check_deserialization;
5
6const MIN_FRC: u16 = 400;
7const MAX_FRC: u16 = 2000;
8const FRC_VAL: &str = "Forced recalibration value";
9const PARTICLE_UNIT: &str = "ppm";
10
11/// A runtime checked representation of the forced recalibration value. Accepted value range:
12/// [400...2000] ppm.
13#[derive(Debug, PartialEq)]
14pub struct ForcedRecalibrationValue(u16);
15
16#[cfg(feature = "defmt")]
17impl defmt::Format for ForcedRecalibrationValue {
18    fn format(&self, f: defmt::Formatter) {
19        defmt::write!(f, "{}ppm", self.0)
20    }
21}
22
23impl ForcedRecalibrationValue {
24    /// Returns a big endian byte representation of the forced recalibration value.
25    pub const fn to_be_bytes(&self) -> [u8; 2] {
26        self.0.to_be_bytes()
27    }
28}
29
30impl TryFrom<u16> for ForcedRecalibrationValue {
31    type Error = DataError;
32
33    /// Converts a u16 value to a [ForcedRecalibrationValue]. The value must be between 2 and 1800 in s.
34    ///
35    /// # Errors
36    ///
37    /// - [ValueOutOfRange](crate::error::DataError::ValueOutOfRange) if `frc` is lower than 400 or higher than 2000 ppm.
38    fn try_from(frc: u16) -> Result<Self, Self::Error> {
39        if !(MIN_FRC..=MAX_FRC).contains(&frc) {
40            Err(DataError::ValueOutOfRange {
41                parameter: FRC_VAL,
42                min: MIN_FRC,
43                max: MAX_FRC,
44                unit: PARTICLE_UNIT,
45            })
46        } else {
47            Ok(Self(frc))
48        }
49    }
50}
51
52impl TryFrom<&[u8]> for ForcedRecalibrationValue {
53    type Error = DataError;
54
55    /// Converts buffered data to a [ForcedRecalibrationValue] value.
56    ///
57    /// # Errors
58    ///
59    /// - [ReceivedBufferWrongSize](crate::error::DataError::ReceivedBufferWrongSize) if the `data` buffer is not big enough for the data
60    ///   that should have been received.
61    /// - [CrcFailed](crate::error::DataError::CrcFailed) if the CRC of the received data does not match.
62    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
63        check_deserialization(data, 3)?;
64        Ok(Self(BigEndian::read_u16(&data[..2])))
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn deserialize_sample_works() {
74        let data = [0x01, 0xC2, 0x50];
75        let frc = ForcedRecalibrationValue::try_from(&data[..]).unwrap();
76        assert_eq!(frc, ForcedRecalibrationValue(450));
77    }
78
79    #[test]
80    fn serialize_sample_works() {
81        let frc = ForcedRecalibrationValue(450);
82        assert_eq!(frc.to_be_bytes(), [0x01, 0xC2]);
83    }
84
85    #[test]
86    fn create_allowed_value_from_u16_works() {
87        let values = [400, 1200, 2000];
88        for value in values {
89            assert_eq!(
90                ForcedRecalibrationValue::try_from(value).unwrap(),
91                ForcedRecalibrationValue(value)
92            );
93        }
94    }
95
96    #[test]
97    fn create_from_u16_non_null_out_of_spec_value_errors() {
98        let values = [300, 2100];
99        for value in values {
100            assert_eq!(
101                ForcedRecalibrationValue::try_from(value).unwrap_err(),
102                DataError::ValueOutOfRange {
103                    parameter: FRC_VAL,
104                    min: 400,
105                    max: 2000,
106                    unit: PARTICLE_UNIT
107                }
108            );
109        }
110    }
111}