1use std::collections::{HashMap, HashSet};
4use std::io::{Error, ErrorKind};
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9use super::Result;
10
11#[derive(Clone, Debug, Deserialize, Serialize)]
13pub struct DhtDataRaw {
14 pub t: f32,
15 pub h: f32,
16 pub hi: f32,
17}
18
19#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Serialize)]
21pub struct SensorData {
22 pub temperature: f32,
23 pub humidity: f32,
24 pub heat_index: f32,
25}
26
27impl From<DhtDataRaw> for SensorData {
29 fn from(data: DhtDataRaw) -> Self {
30 SensorData {
31 temperature: data.t,
32 humidity: data.h,
33 heat_index: data.hi,
34 }
35 }
36}
37
38#[derive(Debug, Deserialize, Serialize)]
42pub struct DhtSensors {
43 pub timestamp: DateTime<Utc>,
44 pub data: HashMap<String, SensorData>,
45}
46
47impl DhtSensors {
48 pub fn from_serde(data: DhtSensorsSerde) -> Result<DhtSensors> {
52 let mut lengths = HashSet::new();
53 lengths.insert(data.o.len());
54 lengths.insert(data.t.len());
55 lengths.insert(data.h.len());
56 lengths.insert(data.hi.len());
57
58 if lengths.len() != 1 {
59 return Err(Error::new(
60 ErrorKind::InvalidData,
61 "length mismatch in serde data",
62 ));
63 }
64
65 let mut sensor_data = HashMap::new();
66 for (i, key) in data.o.iter().enumerate() {
67 sensor_data.insert(
68 key.clone(),
69 SensorData {
70 temperature: data.t[i],
71 humidity: data.h[i],
72 heat_index: data.hi[i],
73 },
74 );
75 }
76
77 Ok(DhtSensors {
78 timestamp: data.ts,
79 data: sensor_data,
80 })
81 }
82}
83
84#[derive(Debug, Deserialize, Serialize)]
88pub struct DhtSensorsSerde {
89 pub ts: DateTime<Utc>,
90 pub o: Vec<String>,
91 pub t: Vec<f32>,
92 pub h: Vec<f32>,
93 pub hi: Vec<f32>,
94}
95
96impl From<DhtSensors> for DhtSensorsSerde {
97 fn from(data: DhtSensors) -> DhtSensorsSerde {
98 let timestamp = data.timestamp;
99 let mut order = Vec::new();
100 let mut temperature = Vec::new();
101 let mut humidity = Vec::new();
102 let mut heat_index = Vec::new();
103
104 for (key, value) in data.data.iter() {
105 order.push(key.clone());
106 temperature.push(value.temperature);
107 humidity.push(value.humidity);
108 heat_index.push(value.heat_index);
109 }
110
111 DhtSensorsSerde {
112 ts: timestamp,
113 o: order,
114 t: temperature,
115 h: humidity,
116 hi: heat_index,
117 }
118 }
119}
120
121union DhtDataUnion<'a> {
122 error: &'a str,
123 data: SensorData,
124}
125
126pub struct Measurement<'a> {
150 error: bool,
151 data: DhtDataUnion<'a>,
152}
153
154impl<'a> Measurement<'a> {
155 pub fn new(data: Option<SensorData>, error: Option<&'a str>) -> Measurement {
161 if (data.is_some() && error.is_some()) || (data.is_none() && error.is_none()) {
162 panic!("Exactly one of data or error must be a Some type.");
163 }
164
165 if let Some(data) = data {
166 return Measurement {
167 error: false,
168 data: DhtDataUnion { data },
169 };
170 }
171
172 if let Some(error) = error {
173 return Measurement {
174 error: true,
175 data: DhtDataUnion { error },
176 };
177 }
178
179 Measurement {
181 error: true,
182 data: DhtDataUnion {
183 error: "initialization error",
184 },
185 }
186 }
187
188 pub fn get_data(&self) -> Option<SensorData> {
190 if self.has_data() {
191 unsafe { Some(self.data.data) }
192 } else {
193 None
194 }
195 }
196
197 pub fn get_error(&self) -> Option<&'a str> {
199 if self.has_error() {
200 unsafe { Some(self.data.error) }
201 } else {
202 None
203 }
204 }
205
206 pub fn has_data(&self) -> bool {
208 !self.error
209 }
210
211 pub fn has_error(&self) -> bool {
213 self.error
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220 #[test]
222 #[should_panic]
223 fn test_measurement_new_both_none() {
224 Measurement::new(None, None);
225 }
226
227 #[test]
229 #[should_panic]
230 fn test_measurement_new_both_some() {
231 let error = "test";
232 let data = SensorData {
233 temperature: 0.0,
234 humidity: 0.0,
235 heat_index: 0.0,
236 };
237 Measurement::new(Some(data), Some(error));
238 }
239
240 #[test]
242 fn test_convert_from_raw() {
243 let raw = DhtDataRaw {
244 t: 21.3,
245 h: 52.7,
246 hi: 22.8,
247 };
248
249 let data = SensorData::from(raw.clone());
250 assert_eq!(raw.t, data.temperature);
251 assert_eq!(raw.h, data.humidity);
252 assert_eq!(raw.hi, data.heat_index);
253 }
254}