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}