bo4e_core/com/
profile_data.rs

1//! Profile data (Profildaten) component.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::enums::Unit;
7use crate::traits::{Bo4eMeta, Bo4eObject};
8
9/// Profile data for standard load profiles or individual consumption profiles.
10///
11/// German: Profildaten
12///
13/// # Example
14///
15/// ```rust
16/// use bo4e_core::com::ProfileData;
17/// use chrono::Utc;
18///
19/// let profile = ProfileData {
20///     profile_type: Some("H0".to_string()),
21///     timestamp: Some(Utc::now()),
22///     value: Some(0.000125),
23///     ..Default::default()
24/// };
25/// ```
26#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
27#[serde(rename_all = "camelCase")]
28pub struct ProfileData {
29    /// BO4E metadata
30    #[serde(flatten)]
31    pub meta: Bo4eMeta,
32
33    /// Profile type/identifier (Profiltyp)
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub profile_type: Option<String>,
36
37    /// Timestamp of the profile value (Zeitpunkt)
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub timestamp: Option<DateTime<Utc>>,
40
41    /// Profile value (Profilwert)
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub value: Option<f64>,
44
45    /// Unit of the profile value (Einheit)
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub unit: Option<Unit>,
48
49    /// Profile name (Profilname)
50    #[serde(skip_serializing_if = "Option::is_none")]
51    pub profile_name: Option<String>,
52
53    /// Profile version (Profilversion)
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub profile_version: Option<String>,
56
57    /// Temperature zone (Temperaturzone)
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub temperature_zone: Option<String>,
60}
61
62impl Bo4eObject for ProfileData {
63    fn type_name_german() -> &'static str {
64        "Profildaten"
65    }
66
67    fn type_name_english() -> &'static str {
68        "ProfileData"
69    }
70
71    fn meta(&self) -> &Bo4eMeta {
72        &self.meta
73    }
74
75    fn meta_mut(&mut self) -> &mut Bo4eMeta {
76        &mut self.meta
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83    use chrono::TimeZone;
84
85    #[test]
86    fn test_profile_data() {
87        let profile = ProfileData {
88            profile_type: Some("H0".to_string()),
89            timestamp: Some(Utc.with_ymd_and_hms(2024, 1, 15, 12, 15, 0).unwrap()),
90            value: Some(0.000125),
91            profile_name: Some("Haushalt".to_string()),
92            ..Default::default()
93        };
94
95        let json = serde_json::to_string(&profile).unwrap();
96        assert!(json.contains("H0"));
97        assert!(json.contains("0.000125"));
98    }
99
100    #[test]
101    fn test_business_profile() {
102        let profile = ProfileData {
103            profile_type: Some("G0".to_string()),
104            profile_name: Some("Gewerbe allgemein".to_string()),
105            profile_version: Some("2024-01".to_string()),
106            temperature_zone: Some("TZ1".to_string()),
107            ..Default::default()
108        };
109
110        let json = serde_json::to_string(&profile).unwrap();
111        assert!(json.contains("G0"));
112        assert!(json.contains("TZ1"));
113    }
114
115    #[test]
116    fn test_roundtrip() {
117        let profile = ProfileData {
118            profile_type: Some("L0".to_string()),
119            value: Some(0.000089),
120            ..Default::default()
121        };
122
123        let json = serde_json::to_string(&profile).unwrap();
124        let parsed: ProfileData = serde_json::from_str(&json).unwrap();
125        assert_eq!(profile, parsed);
126    }
127
128    #[test]
129    fn test_bo4e_object_impl() {
130        assert_eq!(ProfileData::type_name_german(), "Profildaten");
131        assert_eq!(ProfileData::type_name_english(), "ProfileData");
132    }
133}