bo4e_core/com/
substitution_value.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5
6use crate::enums::Unit;
7use crate::traits::{Bo4eMeta, Bo4eObject};
8
9#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
29#[serde(rename_all = "camelCase")]
30pub struct SubstitutionValue {
31 #[serde(flatten)]
33 pub meta: Bo4eMeta,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub timestamp: Option<DateTime<Utc>>,
38
39 #[serde(skip_serializing_if = "Option::is_none")]
41 pub value: Option<f64>,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub unit: Option<Unit>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub substitution_method: Option<String>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub reason: Option<String>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub original_value: Option<f64>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub obis_code: Option<String>,
62}
63
64impl Bo4eObject for SubstitutionValue {
65 fn type_name_german() -> &'static str {
66 "Ersatzwert"
67 }
68
69 fn type_name_english() -> &'static str {
70 "SubstitutionValue"
71 }
72
73 fn meta(&self) -> &Bo4eMeta {
74 &self.meta
75 }
76
77 fn meta_mut(&mut self) -> &mut Bo4eMeta {
78 &mut self.meta
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use chrono::TimeZone;
86
87 #[test]
88 fn test_substitution_value() {
89 let value = SubstitutionValue {
90 timestamp: Some(Utc.with_ymd_and_hms(2024, 1, 15, 12, 0, 0).unwrap()),
91 value: Some(125.5),
92 unit: Some(Unit::KilowattHour),
93 substitution_method: Some("Interpolation".to_string()),
94 reason: Some("Meter communication failure".to_string()),
95 ..Default::default()
96 };
97
98 let json = serde_json::to_string(&value).unwrap();
99 assert!(json.contains("125.5"));
100 assert!(json.contains("Interpolation"));
101 }
102
103 #[test]
104 fn test_with_original() {
105 let value = SubstitutionValue {
106 value: Some(100.0),
107 original_value: Some(-50.0),
108 reason: Some("Negative value not allowed".to_string()),
109 ..Default::default()
110 };
111
112 let json = serde_json::to_string(&value).unwrap();
113 assert!(json.contains("100"));
114 assert!(json.contains("-50"));
115 }
116
117 #[test]
118 fn test_roundtrip() {
119 let value = SubstitutionValue {
120 timestamp: Some(Utc::now()),
121 value: Some(999.99),
122 substitution_method: Some("Historical average".to_string()),
123 ..Default::default()
124 };
125
126 let json = serde_json::to_string(&value).unwrap();
127 let parsed: SubstitutionValue = serde_json::from_str(&json).unwrap();
128 assert_eq!(value.value, parsed.value);
129 assert_eq!(value.substitution_method, parsed.substitution_method);
130 }
131
132 #[test]
133 fn test_bo4e_object_impl() {
134 assert_eq!(SubstitutionValue::type_name_german(), "Ersatzwert");
135 assert_eq!(SubstitutionValue::type_name_english(), "SubstitutionValue");
136 }
137}