Skip to main content

frequenz_microgrid_component_graph/
graph_traits.rs

1// License: MIT
2// Copyright © 2024 Frequenz Energy-as-a-Service GmbH
3
4//! This module contains the traits that need to be implemented by the types
5//! that represent a node and an edge.
6
7use crate::component_category::ComponentCategory;
8use crate::operational_mode::OperationalMode;
9
10/**
11This trait needs to be implemented by the type that represents a node.
12
13Read more about why this is necessary [here][crate#the-node-and-edge-traits].
14
15<details>
16<summary>Example implementation for microgrid API v0.18.1:</summary>
17
18```ignore
19impl frequenz_microgrid_component_graph::Node
20    for common::v1alpha8::microgrid::electrical_components::ElectricalComponent
21{
22    fn component_id(&self) -> u64 {
23        self.id
24    }
25
26    fn category(&self) -> frequenz_microgrid_component_graph::ComponentCategory {
27        use common::v1alpha8::microgrid::electrical_components as pb;
28        use frequenz_microgrid_component_graph as gr;
29
30        let category = pb::ElectricalComponentCategory::try_from(self.category)
31            .unwrap_or_else(|e| {
32                error!("Error converting component category: {}", e);
33                pb::ElectricalComponentCategory::Unspecified
34            });
35
36        let specific_info = self.category_specific_info.as_ref().and_then(|info| info.kind);
37
38        match category {
39            pb::ElectricalComponentCategory::Unspecified => gr::ComponentCategory::Unspecified,
40            pb::ElectricalComponentCategory::GridConnectionPoint => {
41                gr::ComponentCategory::GridConnectionPoint
42            }
43            pb::ElectricalComponentCategory::Meter => gr::ComponentCategory::Meter,
44            pb::ElectricalComponentCategory::Inverter => {
45                use pb::electrical_component_category_specific_info::Kind;
46                gr::ComponentCategory::Inverter(match specific_info {
47                    Some(Kind::Inverter(inverter)) => {
48                        match pb::InverterType::try_from(inverter.r#type).unwrap_or_else(|e| {
49                            error!("Error converting inverter type: {}", e);
50                            pb::InverterType::Unspecified
51                        }) {
52                            pb::InverterType::Pv => gr::InverterType::Pv,
53                            pb::InverterType::Battery => gr::InverterType::Battery,
54                            pb::InverterType::Hybrid => gr::InverterType::Hybrid,
55                            pb::InverterType::Unspecified => gr::InverterType::Unspecified,
56                        }
57                    }
58                    Some(other) => {
59                        warn!("Unknown category-specific info for inverter: {:?}", other);
60                        gr::InverterType::Unspecified
61                    }
62                    None => gr::InverterType::Unspecified,
63                })
64            }
65            pb::ElectricalComponentCategory::Converter => gr::ComponentCategory::Converter,
66            pb::ElectricalComponentCategory::Battery => {
67                use pb::electrical_component_category_specific_info::Kind;
68                gr::ComponentCategory::Battery(match specific_info {
69                    Some(Kind::Battery(battery)) => {
70                        match pb::BatteryType::try_from(battery.r#type).unwrap_or_else(|e| {
71                            error!("Error converting battery type: {}", e);
72                            pb::BatteryType::Unspecified
73                        }) {
74                            pb::BatteryType::LiIon => gr::BatteryType::LiIon,
75                            pb::BatteryType::NaIon => gr::BatteryType::NaIon,
76                            pb::BatteryType::Unspecified => gr::BatteryType::Unspecified,
77                        }
78                    }
79                    Some(other) => {
80                        warn!("Unknown category-specific info for battery: {:?}", other);
81                        gr::BatteryType::Unspecified
82                    }
83                    None => gr::BatteryType::Unspecified,
84                })
85            }
86            pb::ElectricalComponentCategory::EvCharger => {
87                use pb::electrical_component_category_specific_info::Kind;
88                gr::ComponentCategory::EvCharger(match specific_info {
89                    Some(Kind::EvCharger(ev_charger)) => {
90                        match pb::EvChargerType::try_from(ev_charger.r#type).unwrap_or_else(|e| {
91                            error!("Error converting ev charger type: {}", e);
92                            pb::EvChargerType::Unspecified
93                        }) {
94                            pb::EvChargerType::Ac => gr::EvChargerType::Ac,
95                            pb::EvChargerType::Dc => gr::EvChargerType::Dc,
96                            pb::EvChargerType::Hybrid => gr::EvChargerType::Hybrid,
97                            pb::EvChargerType::Unspecified => gr::EvChargerType::Unspecified,
98                        }
99                    }
100                    Some(other) => {
101                        warn!("Unknown category-specific info for ev charger: {:?}", other);
102                        gr::EvChargerType::Unspecified
103                    }
104                    None => gr::EvChargerType::Unspecified,
105                })
106            }
107            pb::ElectricalComponentCategory::Breaker => gr::ComponentCategory::Breaker,
108            pb::ElectricalComponentCategory::Precharger => gr::ComponentCategory::Precharger,
109            pb::ElectricalComponentCategory::Chp => gr::ComponentCategory::Chp,
110            pb::ElectricalComponentCategory::Electrolyzer => gr::ComponentCategory::Electrolyzer,
111            pb::ElectricalComponentCategory::PowerTransformer => {
112                gr::ComponentCategory::PowerTransformer
113            }
114            pb::ElectricalComponentCategory::Hvac => gr::ComponentCategory::Hvac,
115            pb::ElectricalComponentCategory::Plc => gr::ComponentCategory::Plc,
116            pb::ElectricalComponentCategory::CryptoMiner => gr::ComponentCategory::CryptoMiner,
117            pb::ElectricalComponentCategory::StaticTransferSwitch => {
118                gr::ComponentCategory::StaticTransferSwitch
119            }
120            pb::ElectricalComponentCategory::UninterruptiblePowerSupply => {
121                gr::ComponentCategory::UninterruptiblePowerSupply
122            }
123            pb::ElectricalComponentCategory::CapacitorBank => {
124                gr::ComponentCategory::CapacitorBank
125            }
126            pb::ElectricalComponentCategory::WindTurbine => gr::ComponentCategory::WindTurbine,
127            pb::ElectricalComponentCategory::SteamBoiler => gr::ComponentCategory::SteamBoiler,
128        }
129    }
130
131    fn operational_mode(&self) -> frequenz_microgrid_component_graph::OperationalMode {
132        use common::v1alpha8::microgrid::electrical_components as pb;
133        use frequenz_microgrid_component_graph as gr;
134
135        let mode = pb::ElectricalComponentOperationalMode::try_from(self.operational_mode)
136            .unwrap_or_else(|e| {
137                error!("Error converting operational mode: {}", e);
138                pb::ElectricalComponentOperationalMode::Unspecified
139            });
140
141        match mode {
142            pb::ElectricalComponentOperationalMode::Unspecified => {
143                gr::OperationalMode::Unspecified
144            }
145            pb::ElectricalComponentOperationalMode::Inactive => gr::OperationalMode::Inactive,
146            pb::ElectricalComponentOperationalMode::TelemetryOnly => {
147                gr::OperationalMode::TelemetryOnly
148            }
149            pb::ElectricalComponentOperationalMode::ControlOnly => {
150                gr::OperationalMode::ControlOnly
151            }
152            pb::ElectricalComponentOperationalMode::ControlAndTelemetry => {
153                gr::OperationalMode::ControlAndTelemetry
154            }
155        }
156    }
157}
158```
159
160</details>
161*/
162pub trait Node {
163    /// Returns the component id of the component.
164    fn component_id(&self) -> u64;
165    /// Returns the category of the category.
166    fn category(&self) -> ComponentCategory;
167    /// Returns the operational mode of the component.
168    ///
169    /// The default implementation returns [`OperationalMode::Unspecified`],
170    /// which is treated as providing telemetry. An implementor that does not
171    /// override this method keeps every component usable as a measurement
172    /// source in formulas.
173    ///
174    /// A component whose mode does not provide telemetry (see
175    /// [`OperationalMode::provides_telemetry`]) is not used as a measurement
176    /// source in formulas. It is still used to classify the meter that
177    /// measures it (e.g. as a PV meter or a CHP meter).
178    fn operational_mode(&self) -> OperationalMode {
179        OperationalMode::Unspecified
180    }
181}
182
183/**
184This trait needs to be implemented by the type that represents a connection.
185
186Read more about why this is necessary [here][crate#the-node-and-edge-traits].
187
188<details>
189<summary>Example implementation for microgrid API v0.18.1:</summary>
190
191```ignore
192impl frequenz_microgrid_component_graph::Edge
193    for common::v1alpha8::microgrid::electrical_components::ElectricalComponentConnection
194{
195    fn source(&self) -> u64 {
196        self.source_electrical_component_id
197    }
198
199    fn destination(&self) -> u64 {
200        self.destination_electrical_component_id
201    }
202}
203```
204
205</details>
206*/
207pub trait Edge {
208    /// Returns the source component id of the connection.
209    fn source(&self) -> u64;
210    /// Returns the destination component id of the connection.
211    fn destination(&self) -> u64;
212}