1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! Module defining common sensor functionality.

mod account_balance;
mod barometer;
mod beverage_supply;
mod door_locked;
mod humidity;
mod network_connections;
mod network_traffic;
mod people_now_present;
mod power_consumption;
mod radiation;
mod temperature;
mod total_member_count;
mod wind;

pub use account_balance::{AccountBalanceSensor, AccountBalanceSensorTemplate};
pub use barometer::{BarometerSensor, BarometerSensorTemplate};
pub use beverage_supply::{BeverageSupplySensor, BeverageSupplySensorTemplate};
pub use door_locked::{DoorLockedSensor, DoorLockedSensorTemplate};
pub use humidity::{HumiditySensor, HumiditySensorTemplate};
pub use network_connections::{
    NetworkConnectionKind, NetworkConnectionMachine, NetworkConnectionsSensor,
    NetworkConnectionsSensorTemplate,
};
pub use network_traffic::{
    NetworkTrafficBitsPerSecond, NetworkTrafficPacketsPerSecond, NetworkTrafficSensor,
    NetworkTrafficSensorProperties,
};
pub use people_now_present::{PeopleNowPresentSensor, PeopleNowPresentSensorTemplate};
pub use power_consumption::{PowerConsumptionSensor, PowerConsumptionSensorTemplate};
pub use radiation::{RadiationSensor, RadiationSensorUnit, RadiationSensors};
pub use temperature::{TemperatureSensor, TemperatureSensorTemplate};
pub use total_member_count::{TotalMemberCountSensor, TotalMemberCountSensorTemplate};
pub use wind::{WindSensor, WindSensorMeasurement, WindSensorProperties};

use log::warn;
use serde::{Deserialize, Serialize};
use thiserror::Error;

/// Common information describing any sensor.
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
pub struct SensorMetadata {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub location: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
}

/// Common information describing any sensor which requires a specified location.
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
pub struct SensorMetadataWithLocation {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    pub location: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
}

/// Describes an error occurring when building a sensor from a `SensorTemplate`.
#[derive(Error, Debug)]
pub enum SensorTemplateError {
    /// Failed when parsing an integer value from the provided value string
    #[error("sensor integer value cannot be parsed")]
    BadInteger(#[from] std::num::ParseIntError),

    /// Failed when parsing a floating point value from the provided value string
    #[error("sensor float value cannot be parsed")]
    BadFloat(#[from] std::num::ParseFloatError),

    /// Failed when parsing a boolean value from the provided value string
    #[error("sensor boolean value cannot be parsed")]
    BadBool(#[from] std::str::ParseBoolError),
}

/// Trait that allows sensors to be created from a template and string value.
pub trait FromSensorTemplate<T> {
    fn try_from_template(template: &T, value: &str) -> Result<Self, SensorTemplateError>
    where
        Self: Sized;
}

/// A trait for all possible sensor templates.
///
/// A sensor template is like a sensor struct, but without the actual data in it.
/// A `SensorTemplate` is capable of registering itself in a `Sensors` struct.
pub trait SensorTemplate: Send + Sync {
    fn to_sensor(&self, value_str: &str, sensors: &mut Sensors) {
        if let Err(e) = self.try_to_sensor(value_str, sensors) {
            warn!("Omitting sensor. Reason: {}", e);
        }
    }

    fn try_to_sensor(&self, value_str: &str, sensors: &mut Sensors) -> Result<(), SensorTemplateError>;
}

/// Container for instances of all sensor types.
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Sensors {
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub temperature: Vec<TemperatureSensor>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub door_locked: Vec<DoorLockedSensor>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub barometer: Vec<BarometerSensor>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub radiation: Option<RadiationSensors>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub humidity: Vec<HumiditySensor>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub beverage_supply: Vec<BeverageSupplySensor>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub power_consumption: Vec<PowerConsumptionSensor>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub wind: Vec<WindSensor>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub network_connections: Vec<NetworkConnectionsSensor>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub account_balance: Vec<AccountBalanceSensor>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub total_member_count: Vec<TotalMemberCountSensor>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub people_now_present: Vec<PeopleNowPresentSensor>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub network_traffic: Vec<NetworkTrafficSensor>,
}

#[cfg(test)]
mod test {
    use super::*;
    use serde_json::{from_str, to_string};

    #[test]
    fn serialize_deserialize_sensors() {
        let a = Sensors::default();
        let b: Sensors = from_str(&to_string(&a).unwrap()).unwrap();
        assert_eq!(a, b);
    }
}