bo4e_core/bo/
device.rs

1//! Device (Geraet) business object.
2//!
3//! Represents a technical device in the energy infrastructure.
4
5use serde::{Deserialize, Serialize};
6
7use crate::enums::{DeviceCategory, DeviceType};
8use crate::traits::{Bo4eMeta, Bo4eObject};
9
10/// A technical device used in the energy infrastructure.
11///
12/// German: Geraet
13///
14/// Devices are technical equipment like transformers, switches,
15/// or other equipment in the energy network.
16///
17/// # Example
18///
19/// ```rust
20/// use bo4e_core::bo::Device;
21/// use bo4e_core::enums::DeviceCategory;
22///
23/// let device = Device {
24///     device_id: Some("DEV001".to_string()),
25///     device_category: Some(DeviceCategory::MeteringDevice),
26///     ..Default::default()
27/// };
28/// ```
29#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct Device {
32    /// BO4E metadata
33    #[serde(flatten)]
34    pub meta: Bo4eMeta,
35
36    /// Device identification (Geraetkennung)
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub device_id: Option<String>,
39
40    /// Serial number (Seriennummer)
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub serial_number: Option<String>,
43
44    /// Device category (Geraeteklasse)
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub device_category: Option<DeviceCategory>,
47
48    /// Device type (Geraetetyp)
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub device_type: Option<DeviceType>,
51
52    /// Manufacturer (Hersteller)
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub manufacturer: Option<String>,
55
56    /// Model name (Modellbezeichnung)
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub model: Option<String>,
59
60    /// Manufacturing year (Baujahr)
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub manufacturing_year: Option<i32>,
63
64    /// Installation date (Einbaudatum)
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub installation_date: Option<chrono::DateTime<chrono::Utc>>,
67
68    /// Removal date (Ausbaudatum)
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub removal_date: Option<chrono::DateTime<chrono::Utc>>,
71
72    /// Firmware version (Firmware-Version)
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub firmware_version: Option<String>,
75
76    /// Description (Beschreibung)
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub description: Option<String>,
79
80    /// Associated metering location ID
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub metering_location_id: Option<String>,
83}
84
85impl Bo4eObject for Device {
86    fn type_name_german() -> &'static str {
87        "Geraet"
88    }
89
90    fn type_name_english() -> &'static str {
91        "Device"
92    }
93
94    fn meta(&self) -> &Bo4eMeta {
95        &self.meta
96    }
97
98    fn meta_mut(&mut self) -> &mut Bo4eMeta {
99        &mut self.meta
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn test_device_creation() {
109        let device = Device {
110            device_id: Some("DEV001".to_string()),
111            device_category: Some(DeviceCategory::MeteringDevice),
112            ..Default::default()
113        };
114
115        assert_eq!(device.device_id, Some("DEV001".to_string()));
116    }
117
118    #[test]
119    fn test_serialize() {
120        let device = Device {
121            meta: Bo4eMeta::with_type("Geraet"),
122            device_id: Some("DEV001".to_string()),
123            ..Default::default()
124        };
125
126        let json = serde_json::to_string(&device).unwrap();
127        assert!(json.contains(r#""_typ":"Geraet""#));
128    }
129
130    #[test]
131    fn test_roundtrip() {
132        let device = Device {
133            meta: Bo4eMeta::with_type("Geraet"),
134            device_id: Some("DEV001".to_string()),
135            serial_number: Some("SN123456".to_string()),
136            device_category: Some(DeviceCategory::MeteringDevice),
137            manufacturer: Some("Acme Corp".to_string()),
138            manufacturing_year: Some(2023),
139            ..Default::default()
140        };
141
142        let json = serde_json::to_string(&device).unwrap();
143        let parsed: Device = serde_json::from_str(&json).unwrap();
144        assert_eq!(device, parsed);
145    }
146
147    #[test]
148    fn test_bo4e_object_impl() {
149        assert_eq!(Device::type_name_german(), "Geraet");
150        assert_eq!(Device::type_name_english(), "Device");
151    }
152}