Skip to main content

bo4e_core/bo/
technical_resource.rs

1//! Technical resource (TechnischeRessource) business object.
2//!
3//! Represents a technical resource in the energy infrastructure.
4
5use serde::{Deserialize, Serialize};
6
7use crate::com::Address;
8use crate::enums::{Division, EnergyDirection, TechnicalResourceUsage};
9use crate::traits::{Bo4eMeta, Bo4eObject};
10
11/// A technical resource in the energy infrastructure.
12///
13/// German: TechnischeRessource
14///
15/// Technical resources are physical components that produce,
16/// consume, or store energy.
17///
18/// # Example
19///
20/// ```rust
21/// use bo4e_core::bo::TechnicalResource;
22/// use bo4e_core::enums::{Division, TechnicalResourceUsage};
23///
24/// let resource = TechnicalResource {
25///     technical_resource_id: Some("TR001".to_string()),
26///     division: Some(Division::Electricity),
27///     usage: Some(TechnicalResourceUsage::ElectricityGenerationType),
28///     ..Default::default()
29/// };
30/// ```
31#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
32#[cfg_attr(feature = "json-schema", derive(schemars::JsonSchema))]
33#[cfg_attr(feature = "json-schema", schemars(rename = "TechnischeRessource"))]
34#[serde(rename_all = "camelCase")]
35pub struct TechnicalResource {
36    /// BO4E metadata
37    #[serde(flatten)]
38    pub meta: Bo4eMeta,
39
40    /// Technical resource ID (TechnischeRessource-ID)
41    #[serde(skip_serializing_if = "Option::is_none")]
42    #[cfg_attr(feature = "json-schema", schemars(rename = "technischeRessourceId"))]
43    pub technical_resource_id: Option<String>,
44
45    /// Energy division (Sparte)
46    #[serde(skip_serializing_if = "Option::is_none")]
47    #[cfg_attr(feature = "json-schema", schemars(rename = "sparte"))]
48    pub division: Option<Division>,
49
50    /// Usage type (Verwendungszweck)
51    #[serde(skip_serializing_if = "Option::is_none")]
52    #[cfg_attr(feature = "json-schema", schemars(rename = "verwendungszweck"))]
53    pub usage: Option<TechnicalResourceUsage>,
54
55    /// Energy direction (Energierichtung)
56    #[serde(skip_serializing_if = "Option::is_none")]
57    #[cfg_attr(feature = "json-schema", schemars(rename = "energierichtung"))]
58    pub energy_direction: Option<EnergyDirection>,
59
60    /// Location address (Standort)
61    #[serde(skip_serializing_if = "Option::is_none")]
62    #[cfg_attr(feature = "json-schema", schemars(rename = "standort"))]
63    pub address: Option<Address>,
64
65    /// Description (Beschreibung)
66    #[serde(skip_serializing_if = "Option::is_none")]
67    #[cfg_attr(feature = "json-schema", schemars(rename = "beschreibung"))]
68    pub description: Option<String>,
69
70    /// Nominal power in kW (Nennleistung)
71    #[serde(skip_serializing_if = "Option::is_none")]
72    #[cfg_attr(feature = "json-schema", schemars(rename = "nennleistung"))]
73    pub nominal_power: Option<f64>,
74
75    /// Maximum power in kW (Maximalleistung)
76    #[serde(skip_serializing_if = "Option::is_none")]
77    #[cfg_attr(feature = "json-schema", schemars(rename = "maximalleistung"))]
78    pub max_power: Option<f64>,
79
80    /// Minimum power in kW (Minimalleistung)
81    #[serde(skip_serializing_if = "Option::is_none")]
82    #[cfg_attr(feature = "json-schema", schemars(rename = "minimalleistung"))]
83    pub min_power: Option<f64>,
84
85    /// Energy capacity in kWh (Speicherkapazitaet)
86    #[serde(skip_serializing_if = "Option::is_none")]
87    #[cfg_attr(feature = "json-schema", schemars(rename = "speicherkapazitaet"))]
88    pub energy_capacity: Option<f64>,
89
90    /// Associated metering location ID
91    #[serde(skip_serializing_if = "Option::is_none")]
92    #[cfg_attr(feature = "json-schema", schemars(rename = "messlokationsId"))]
93    pub metering_location_id: Option<String>,
94
95    /// Associated market location ID
96    #[serde(skip_serializing_if = "Option::is_none")]
97    #[cfg_attr(feature = "json-schema", schemars(rename = "marktlokationsId"))]
98    pub market_location_id: Option<String>,
99
100    /// Commissioning date (Inbetriebnahmedatum)
101    #[serde(skip_serializing_if = "Option::is_none")]
102    #[cfg_attr(feature = "json-schema", schemars(rename = "inbetriebnahmedatum"))]
103    pub commissioning_date: Option<chrono::DateTime<chrono::Utc>>,
104
105    /// Decommissioning date (Stilllegungsdatum)
106    #[serde(skip_serializing_if = "Option::is_none")]
107    #[cfg_attr(feature = "json-schema", schemars(rename = "stilllegungsdatum"))]
108    pub decommissioning_date: Option<chrono::DateTime<chrono::Utc>>,
109}
110
111impl Bo4eObject for TechnicalResource {
112    fn type_name_german() -> &'static str {
113        "TechnischeRessource"
114    }
115
116    fn type_name_english() -> &'static str {
117        "TechnicalResource"
118    }
119
120    fn meta(&self) -> &Bo4eMeta {
121        &self.meta
122    }
123
124    fn meta_mut(&mut self) -> &mut Bo4eMeta {
125        &mut self.meta
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn test_technical_resource_creation() {
135        let resource = TechnicalResource {
136            technical_resource_id: Some("TR001".to_string()),
137            division: Some(Division::Electricity),
138            usage: Some(TechnicalResourceUsage::ElectricityGenerationType),
139            ..Default::default()
140        };
141
142        assert_eq!(resource.technical_resource_id, Some("TR001".to_string()));
143    }
144
145    #[test]
146    fn test_serialize() {
147        let resource = TechnicalResource {
148            meta: Bo4eMeta::with_type("TechnischeRessource"),
149            technical_resource_id: Some("TR001".to_string()),
150            ..Default::default()
151        };
152
153        let json = serde_json::to_string(&resource).unwrap();
154        assert!(json.contains(r#""_typ":"TechnischeRessource""#));
155    }
156
157    #[test]
158    fn test_roundtrip() {
159        let resource = TechnicalResource {
160            meta: Bo4eMeta::with_type("TechnischeRessource"),
161            technical_resource_id: Some("TR001".to_string()),
162            division: Some(Division::Electricity),
163            usage: Some(TechnicalResourceUsage::ElectricityGenerationType),
164            nominal_power: Some(100.0),
165            max_power: Some(120.0),
166            ..Default::default()
167        };
168
169        let json = serde_json::to_string(&resource).unwrap();
170        let parsed: TechnicalResource = serde_json::from_str(&json).unwrap();
171        assert_eq!(resource, parsed);
172    }
173
174    #[test]
175    fn test_bo4e_object_impl() {
176        assert_eq!(TechnicalResource::type_name_german(), "TechnischeRessource");
177        assert_eq!(TechnicalResource::type_name_english(), "TechnicalResource");
178    }
179}