scd30_interface/data/
measurement_interval.rs

1use byteorder::{BigEndian, ByteOrder};
2
3use crate::{error::DataError, util::check_deserialization};
4
5/// A runtime checked representation of the measurement interval configurable for the
6/// continuous measurements. Accepted value range: [2...1800] s.
7#[derive(Debug, PartialEq)]
8pub struct MeasurementInterval(u16);
9
10const MIN_MEASUREMENT_INTERVAL: u16 = 2;
11const MAX_MEASUREMENT_INTERVAL: u16 = 1800;
12const MEASUREMENT_INTERVAL_VAL: &str = "Measurement interval";
13const INTERVAL_UNIT: &str = "s";
14
15impl MeasurementInterval {
16    /// Returns a big endian byte representation of the measurement interval.
17    pub const fn to_be_bytes(&self) -> [u8; 2] {
18        self.0.to_be_bytes()
19    }
20}
21
22#[cfg(feature = "defmt")]
23impl defmt::Format for MeasurementInterval {
24    fn format(&self, f: defmt::Formatter) {
25        defmt::write!(f, "{}s", self.0)
26    }
27}
28
29impl TryFrom<u16> for MeasurementInterval {
30    type Error = DataError;
31
32    /// Converts a u16 value to a [MeasurementInterval]. The value must be between 2 and 1800 in s.
33    ///
34    /// # Errors
35    ///
36    /// - [ValueOutOfRange](crate::error::DataError::ValueOutOfRange) if `interval` is lower than 2 or higher than
37    ///   1800 s.
38    fn try_from(interval: u16) -> Result<Self, Self::Error> {
39        if !(MIN_MEASUREMENT_INTERVAL..=MAX_MEASUREMENT_INTERVAL).contains(&interval) {
40            Err(DataError::ValueOutOfRange {
41                parameter: MEASUREMENT_INTERVAL_VAL,
42                min: MIN_MEASUREMENT_INTERVAL,
43                max: MAX_MEASUREMENT_INTERVAL,
44                unit: INTERVAL_UNIT,
45            })
46        } else {
47            Ok(Self(interval))
48        }
49    }
50}
51
52impl TryFrom<&[u8]> for MeasurementInterval {
53    type Error = DataError;
54
55    /// Converts buffered data to a [MeasurementInterval].
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 = [0x00, 0x02, 0xE3];
75        let interval = MeasurementInterval::try_from(&data[..]).unwrap();
76        assert_eq!(interval, MeasurementInterval(2));
77    }
78
79    #[test]
80    fn serialize_sample_works() {
81        let interval = MeasurementInterval(2);
82        assert_eq!(interval.to_be_bytes(), [0x00, 0x02]);
83    }
84
85    #[test]
86    fn create_allowed_value_from_u16_works() {
87        let values = [2, 901, 1800];
88        for value in values {
89            assert_eq!(
90                MeasurementInterval::try_from(value).unwrap(),
91                MeasurementInterval(value)
92            );
93        }
94    }
95
96    #[test]
97    fn create_from_u16_non_null_out_of_spec_value_errors() {
98        let values = [1, 2000];
99        for value in values {
100            assert_eq!(
101                MeasurementInterval::try_from(value).unwrap_err(),
102                DataError::ValueOutOfRange {
103                    parameter: MEASUREMENT_INTERVAL_VAL,
104                    min: 2,
105                    max: 1800,
106                    unit: INTERVAL_UNIT
107                }
108            );
109        }
110    }
111}