1use serde::{Deserialize, Serialize};
6
7use crate::com::{Address, Hardware, MeterRegister};
8use crate::enums::{Division, MeterSize, MeterType};
9use crate::traits::{Bo4eMeta, Bo4eObject};
10
11#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
29#[serde(rename_all = "camelCase")]
30pub struct Meter {
31 #[serde(flatten)]
33 pub meta: Bo4eMeta,
34
35 #[serde(skip_serializing_if = "Option::is_none", alias = "zaehlernummer")]
37 pub meter_number: Option<String>,
38
39 #[serde(skip_serializing_if = "Option::is_none", alias = "sparte")]
41 pub division: Option<Division>,
42
43 #[serde(skip_serializing_if = "Option::is_none", alias = "zaehlertyp")]
45 pub meter_type: Option<MeterType>,
46
47 #[serde(skip_serializing_if = "Option::is_none", alias = "zaehlergroesse")]
49 pub meter_size: Option<MeterSize>,
50
51 #[serde(skip_serializing_if = "Option::is_none", alias = "standort")]
53 pub location: Option<Address>,
54
55 #[serde(default, skip_serializing_if = "Vec::is_empty", alias = "zaehlwerke")]
57 pub registers: Vec<MeterRegister>,
58
59 #[serde(
61 default,
62 skip_serializing_if = "Vec::is_empty",
63 alias = "geraeteeigenschaften"
64 )]
65 pub hardware: Vec<Hardware>,
66
67 #[serde(skip_serializing_if = "Option::is_none", alias = "marktlokationsId")]
69 pub market_location_id: Option<String>,
70
71 #[serde(skip_serializing_if = "Option::is_none", alias = "messlokationsId")]
73 pub metering_location_id: Option<String>,
74
75 #[serde(
77 skip_serializing_if = "Option::is_none",
78 alias = "eigentumsverhaeltnis"
79 )]
80 pub ownership: Option<String>,
81
82 #[serde(skip_serializing_if = "Option::is_none", alias = "hersteller")]
84 pub manufacturer: Option<String>,
85
86 #[serde(skip_serializing_if = "Option::is_none", alias = "herstellungsjahr")]
88 pub manufacturing_year: Option<i32>,
89
90 #[serde(skip_serializing_if = "Option::is_none", alias = "einbaudatum")]
92 pub installation_date: Option<chrono::DateTime<chrono::Utc>>,
93
94 #[serde(skip_serializing_if = "Option::is_none", alias = "ausbaudatum")]
96 pub removal_date: Option<chrono::DateTime<chrono::Utc>>,
97
98 #[serde(skip_serializing_if = "Option::is_none", alias = "eichdatum")]
100 pub calibration_date: Option<chrono::DateTime<chrono::Utc>>,
101
102 #[serde(skip_serializing_if = "Option::is_none", alias = "eichablaufdatum")]
104 pub calibration_expiry_date: Option<chrono::DateTime<chrono::Utc>>,
105}
106
107impl Bo4eObject for Meter {
108 fn type_name_german() -> &'static str {
109 "Zaehler"
110 }
111
112 fn type_name_english() -> &'static str {
113 "Meter"
114 }
115
116 fn meta(&self) -> &Bo4eMeta {
117 &self.meta
118 }
119
120 fn meta_mut(&mut self) -> &mut Bo4eMeta {
121 &mut self.meta
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn test_meter_creation() {
131 let meter = Meter {
132 meter_number: Some("1EMH0012345678".to_string()),
133 division: Some(Division::Electricity),
134 ..Default::default()
135 };
136
137 assert_eq!(meter.meter_number, Some("1EMH0012345678".to_string()));
138 assert_eq!(meter.division, Some(Division::Electricity));
139 }
140
141 #[test]
142 fn test_meter_with_registers() {
143 let register = MeterRegister {
144 obis_code: Some("1-0:1.8.0".to_string()),
145 ..Default::default()
146 };
147
148 let meter = Meter {
149 meter_number: Some("TEST123".to_string()),
150 registers: vec![register],
151 ..Default::default()
152 };
153
154 assert_eq!(meter.registers.len(), 1);
155 }
156
157 #[test]
158 fn test_serialize() {
159 let meter = Meter {
160 meta: Bo4eMeta::with_type("Zaehler"),
161 meter_number: Some("1EMH0012345678".to_string()),
162 division: Some(Division::Electricity),
163 ..Default::default()
164 };
165
166 let json = serde_json::to_string(&meter).unwrap();
167 assert!(json.contains(r#""_typ":"Zaehler""#));
168 assert!(json.contains(r#""meterNumber":"1EMH0012345678""#));
169 }
170
171 #[test]
172 fn test_roundtrip() {
173 let meter = Meter {
174 meta: Bo4eMeta::with_type("Zaehler"),
175 meter_number: Some("TEST123".to_string()),
176 division: Some(Division::Electricity),
177 meter_type: Some(MeterType::ModernMeasuringDevice),
178 meter_size: Some(MeterSize::G4),
179 manufacturer: Some("Acme Corp".to_string()),
180 manufacturing_year: Some(2023),
181 ..Default::default()
182 };
183
184 let json = serde_json::to_string(&meter).unwrap();
185 let parsed: Meter = serde_json::from_str(&json).unwrap();
186 assert_eq!(meter, parsed);
187 }
188
189 #[test]
190 fn test_bo4e_object_impl() {
191 assert_eq!(Meter::type_name_german(), "Zaehler");
192 assert_eq!(Meter::type_name_english(), "Meter");
193 }
194}