Skip to content

Commit d988ee9

Browse files
authored
Sync ElectricalComponent with the protobuf message (#216)
The protobuf have a few new and deprecated fields and one existing field had a name that doesn't match the protobuf name, which could lead to confusion. Since this code is new, we completely remove the deprecated fields. Part of #203.
2 parents f019eaf + d9d325f commit d988ee9

17 files changed

Lines changed: 223 additions & 162 deletions

src/frequenz/client/common/microgrid/electrical_components/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
UnspecifiedInverter,
4646
)
4747
from ._meter import Meter
48+
from ._operational_mode import ElectricalComponentOperationalMode
4849
from ._power_transformer import PowerTransformer
4950
from ._precharger import Precharger
5051
from ._problematic import (
@@ -79,6 +80,7 @@
7980
"ElectricalComponentConnection",
8081
"ElectricalComponentDiagnosticCode",
8182
"ElectricalComponentId",
83+
"ElectricalComponentOperationalMode",
8284
"ElectricalComponentStateCode",
8385
"ElectricalComponentTypes",
8486
"Electrolyzer",

src/frequenz/client/common/microgrid/electrical_components/_electrical_component.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from .. import MicrogridId
1414
from ._category import ElectricalComponentCategory
1515
from ._ids import ElectricalComponentId
16+
from ._operational_mode import ElectricalComponentOperationalMode
1617

1718

1819
@dataclasses.dataclass(frozen=True, kw_only=True)
@@ -41,24 +42,37 @@ class ElectricalComponent: # pylint: disable=too-many-instance-attributes
4142
name: str | None = None
4243
"""The name of this electrical component."""
4344

44-
manufacturer: str | None = None
45-
"""The manufacturer of this electrical component."""
45+
model: str | None = None
46+
"""The model of this electrical component.
4647
47-
model_name: str | None = None
48-
"""The model name of this electrical component."""
48+
This includes both the manufacturer and the model name.
49+
"""
4950

5051
operational_lifetime: Lifetime = dataclasses.field(default_factory=Lifetime)
5152
"""The operational lifetime of this electrical component."""
5253

53-
rated_bounds: Mapping[Metric | int, Bounds] = dataclasses.field(
54+
operational_mode: ElectricalComponentOperationalMode | int = (
55+
ElectricalComponentOperationalMode.UNSPECIFIED
56+
)
57+
"""The operational mode of this electrical component.
58+
59+
This indicates whether the component is active and operational, and whether it
60+
provides telemetry data, accepts control commands, or both.
61+
"""
62+
63+
metric_config_bounds: Mapping[Metric | int, Bounds] = dataclasses.field(
5464
default_factory=dict,
5565
# dict is not hashable, so we don't use this field to calculate the hash. This
5666
# shouldn't be a problem since it is very unlikely that two components with all
5767
# other attributes being equal would have different category specific metadata,
5868
# so hash collisions should be still very unlikely.
5969
hash=False,
6070
)
61-
"""List of rated bounds present for the electrical component identified by Metric."""
71+
"""The metric configuration bounds for this electrical component, keyed by metric.
72+
73+
These bounds may be derived from the component configuration, manufacturer
74+
limits, or limits of other devices.
75+
"""
6276

6377
category_specific_metadata: Mapping[str, Any] = dataclasses.field(
6478
default_factory=dict,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# License: MIT
2+
# Copyright © 2026 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Electrical component operational modes."""
5+
6+
import enum
7+
8+
9+
@enum.unique
10+
class ElectricalComponentOperationalMode(enum.Enum):
11+
"""The operational mode of an electrical component.
12+
13+
This indicates whether the component is active and operational, and whether it
14+
provides telemetry data, accepts control commands, or both.
15+
"""
16+
17+
UNSPECIFIED = 0
18+
"""Default value when the operational mode is not explicitly set."""
19+
20+
INACTIVE = 1
21+
"""The component is inactive and not operational.
22+
23+
It does not provide telemetry data, and it does not accept control commands.
24+
"""
25+
26+
TELEMETRY_ONLY = 2
27+
"""The component is active and operational, providing telemetry data only.
28+
29+
It does not accept control commands.
30+
"""
31+
32+
CONTROL_ONLY = 3
33+
"""The component is active and operational, accepting control commands only.
34+
35+
It does not provide telemetry data.
36+
"""
37+
38+
CONTROL_AND_TELEMETRY = 4
39+
"""The component is active and operational.
40+
41+
It provides telemetry data and accepts control commands.
42+
"""

src/frequenz/client/common/microgrid/electrical_components/proto/v1alpha8/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
electrical_component_connection_from_proto,
2020
electrical_component_connection_from_proto_with_issues,
2121
)
22+
from ._operational_mode import (
23+
electrical_component_operational_mode_from_proto,
24+
electrical_component_operational_mode_to_proto,
25+
)
2226
from ._state_code import (
2327
electrical_component_state_code_from_proto,
2428
electrical_component_state_code_to_proto,
@@ -33,6 +37,8 @@
3337
"electrical_component_diagnostic_code_to_proto",
3438
"electrical_component_from_proto",
3539
"electrical_component_from_proto_with_issues",
40+
"electrical_component_operational_mode_from_proto",
41+
"electrical_component_operational_mode_to_proto",
3642
"electrical_component_state_code_from_proto",
3743
"electrical_component_state_code_to_proto",
3844
]

src/frequenz/client/common/microgrid/electrical_components/proto/v1alpha8/_electrical_component.py

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
DcEvCharger,
3030
ElectricalComponentCategory,
3131
ElectricalComponentId,
32+
ElectricalComponentOperationalMode,
3233
ElectricalComponentTypes,
3334
Electrolyzer,
3435
EvChargerType,
@@ -55,6 +56,7 @@
5556
UnspecifiedInverter,
5657
WindTurbine,
5758
)
59+
from ._operational_mode import electrical_component_operational_mode_from_proto
5860

5961
_logger = logging.getLogger(__name__)
6062

@@ -104,15 +106,16 @@ class _ElectricalComponentBaseData(NamedTuple):
104106
component_id: ElectricalComponentId
105107
microgrid_id: MicrogridId
106108
name: str | None
107-
manufacturer: str | None
108-
model_name: str | None
109+
model: str | None
109110
category: ElectricalComponentCategory | int
110111
lifetime: Lifetime
111-
rated_bounds: dict[Metric | int, Bounds]
112+
metric_config_bounds: dict[Metric | int, Bounds]
112113
category_specific_info: dict[str, Any]
114+
operational_mode: ElectricalComponentOperationalMode | int
113115
category_mismatched: bool = False
114116

115117

118+
# pylint: disable-next=too-many-locals
116119
def _electrical_component_base_from_proto_with_issues(
117120
message: electrical_components_pb2.ElectricalComponent,
118121
*,
@@ -136,19 +139,19 @@ def _electrical_component_base_from_proto_with_issues(
136139
if name is None:
137140
minor_issues.append("name is empty")
138141

139-
manufacturer = message.manufacturer or None
140-
if manufacturer is None:
141-
minor_issues.append("manufacturer is empty")
142+
model = message.model or None
143+
if model is None:
144+
minor_issues.append("model is empty")
142145

143-
model_name = message.model_name or None
144-
if model_name is None:
145-
minor_issues.append("model_name is empty")
146+
operational_mode = electrical_component_operational_mode_from_proto(
147+
message.operational_mode
148+
)
146149

147150
lifetime = _get_operational_lifetime_from_proto(
148151
message, major_issues=major_issues, minor_issues=minor_issues
149152
)
150153

151-
rated_bounds = _metric_config_bounds_from_proto(
154+
metric_config_bounds = _metric_config_bounds_from_proto(
152155
message.metric_config_bounds,
153156
major_issues=major_issues,
154157
minor_issues=minor_issues,
@@ -184,12 +187,12 @@ def _electrical_component_base_from_proto_with_issues(
184187
component_id,
185188
microgrid_id,
186189
name,
187-
manufacturer,
188-
model_name,
190+
model,
189191
category,
190192
lifetime,
191-
rated_bounds,
193+
metric_config_bounds,
192194
category_specific_info,
195+
operational_mode,
193196
category_mismatched,
194197
)
195198

@@ -220,12 +223,12 @@ def electrical_component_from_proto_with_issues(
220223
id=base_data.component_id,
221224
microgrid_id=base_data.microgrid_id,
222225
name=base_data.name,
223-
manufacturer=base_data.manufacturer,
224-
model_name=base_data.model_name,
226+
model=base_data.model,
225227
category=base_data.category,
226228
operational_lifetime=base_data.lifetime,
229+
operational_mode=base_data.operational_mode,
227230
category_specific_metadata=base_data.category_specific_info,
228-
rated_bounds=base_data.rated_bounds,
231+
metric_config_bounds=base_data.metric_config_bounds,
229232
)
230233

231234
match base_data.category:
@@ -234,11 +237,11 @@ def electrical_component_from_proto_with_issues(
234237
id=base_data.component_id,
235238
microgrid_id=base_data.microgrid_id,
236239
name=base_data.name,
237-
manufacturer=base_data.manufacturer,
238-
model_name=base_data.model_name,
240+
model=base_data.model,
239241
category=base_data.category,
240242
operational_lifetime=base_data.lifetime,
241-
rated_bounds=base_data.rated_bounds,
243+
operational_mode=base_data.operational_mode,
244+
metric_config_bounds=base_data.metric_config_bounds,
242245
)
243246
case (
244247
ElectricalComponentCategory.UNSPECIFIED
@@ -257,10 +260,10 @@ def electrical_component_from_proto_with_issues(
257260
id=base_data.component_id,
258261
microgrid_id=base_data.microgrid_id,
259262
name=base_data.name,
260-
manufacturer=base_data.manufacturer,
261-
model_name=base_data.model_name,
263+
model=base_data.model,
262264
operational_lifetime=base_data.lifetime,
263-
rated_bounds=base_data.rated_bounds,
265+
operational_mode=base_data.operational_mode,
266+
metric_config_bounds=base_data.metric_config_bounds,
264267
)
265268
case ElectricalComponentCategory.BATTERY:
266269
battery_enum_to_class: dict[
@@ -281,21 +284,21 @@ def electrical_component_from_proto_with_issues(
281284
id=base_data.component_id,
282285
microgrid_id=base_data.microgrid_id,
283286
name=base_data.name,
284-
manufacturer=base_data.manufacturer,
285-
model_name=base_data.model_name,
287+
model=base_data.model,
286288
operational_lifetime=base_data.lifetime,
287-
rated_bounds=base_data.rated_bounds,
289+
operational_mode=base_data.operational_mode,
290+
metric_config_bounds=base_data.metric_config_bounds,
288291
)
289292
case int():
290293
major_issues.append(f"battery type {battery_type} is unrecognized")
291294
return UnrecognizedBattery(
292295
id=base_data.component_id,
293296
microgrid_id=base_data.microgrid_id,
294297
name=base_data.name,
295-
manufacturer=base_data.manufacturer,
296-
model_name=base_data.model_name,
298+
model=base_data.model,
297299
operational_lifetime=base_data.lifetime,
298-
rated_bounds=base_data.rated_bounds,
300+
operational_mode=base_data.operational_mode,
301+
metric_config_bounds=base_data.metric_config_bounds,
299302
type=battery_type,
300303
)
301304
case unexpected_battery_type:
@@ -328,10 +331,10 @@ def electrical_component_from_proto_with_issues(
328331
id=base_data.component_id,
329332
microgrid_id=base_data.microgrid_id,
330333
name=base_data.name,
331-
manufacturer=base_data.manufacturer,
332-
model_name=base_data.model_name,
334+
model=base_data.model,
333335
operational_lifetime=base_data.lifetime,
334-
rated_bounds=base_data.rated_bounds,
336+
operational_mode=base_data.operational_mode,
337+
metric_config_bounds=base_data.metric_config_bounds,
335338
)
336339
case int():
337340
major_issues.append(
@@ -341,10 +344,10 @@ def electrical_component_from_proto_with_issues(
341344
id=base_data.component_id,
342345
microgrid_id=base_data.microgrid_id,
343346
name=base_data.name,
344-
manufacturer=base_data.manufacturer,
345-
model_name=base_data.model_name,
347+
model=base_data.model,
346348
operational_lifetime=base_data.lifetime,
347-
rated_bounds=base_data.rated_bounds,
349+
operational_mode=base_data.operational_mode,
350+
metric_config_bounds=base_data.metric_config_bounds,
348351
type=ev_charger_type,
349352
)
350353
case unexpected_ev_charger_type:
@@ -358,10 +361,10 @@ def electrical_component_from_proto_with_issues(
358361
id=base_data.component_id,
359362
microgrid_id=base_data.microgrid_id,
360363
name=base_data.name,
361-
manufacturer=base_data.manufacturer,
362-
model_name=base_data.model_name,
364+
model=base_data.model,
363365
operational_lifetime=base_data.lifetime,
364-
rated_bounds=base_data.rated_bounds,
366+
operational_mode=base_data.operational_mode,
367+
metric_config_bounds=base_data.metric_config_bounds,
365368
rated_fuse_current=rated_fuse_current,
366369
)
367370
case ElectricalComponentCategory.INVERTER:
@@ -392,10 +395,10 @@ def electrical_component_from_proto_with_issues(
392395
id=base_data.component_id,
393396
microgrid_id=base_data.microgrid_id,
394397
name=base_data.name,
395-
manufacturer=base_data.manufacturer,
396-
model_name=base_data.model_name,
398+
model=base_data.model,
397399
operational_lifetime=base_data.lifetime,
398-
rated_bounds=base_data.rated_bounds,
400+
operational_mode=base_data.operational_mode,
401+
metric_config_bounds=base_data.metric_config_bounds,
399402
)
400403
case int():
401404
major_issues.append(
@@ -405,10 +408,10 @@ def electrical_component_from_proto_with_issues(
405408
id=base_data.component_id,
406409
microgrid_id=base_data.microgrid_id,
407410
name=base_data.name,
408-
manufacturer=base_data.manufacturer,
409-
model_name=base_data.model_name,
411+
model=base_data.model,
410412
operational_lifetime=base_data.lifetime,
411-
rated_bounds=base_data.rated_bounds,
413+
operational_mode=base_data.operational_mode,
414+
metric_config_bounds=base_data.metric_config_bounds,
412415
type=inverter_type,
413416
)
414417
case unexpected_inverter_type:
@@ -418,10 +421,10 @@ def electrical_component_from_proto_with_issues(
418421
id=base_data.component_id,
419422
microgrid_id=base_data.microgrid_id,
420423
name=base_data.name,
421-
manufacturer=base_data.manufacturer,
422-
model_name=base_data.model_name,
424+
model=base_data.model,
423425
operational_lifetime=base_data.lifetime,
424-
rated_bounds=base_data.rated_bounds,
426+
operational_mode=base_data.operational_mode,
427+
metric_config_bounds=base_data.metric_config_bounds,
425428
primary_voltage=message.category_specific_info.power_transformer.primary,
426429
secondary_voltage=message.category_specific_info.power_transformer.secondary,
427430
)
@@ -439,11 +442,11 @@ def electrical_component_from_proto_with_issues(
439442
id=base_data.component_id,
440443
microgrid_id=base_data.microgrid_id,
441444
name=base_data.name,
442-
manufacturer=base_data.manufacturer,
443-
model_name=base_data.model_name,
445+
model=base_data.model,
444446
category=base_data.category.value,
445447
operational_lifetime=base_data.lifetime,
446-
rated_bounds=base_data.rated_bounds,
448+
operational_mode=base_data.operational_mode,
449+
metric_config_bounds=base_data.metric_config_bounds,
447450
)
448451
case unexpected_category:
449452
assert_never(unexpected_category)

0 commit comments

Comments
 (0)