Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
UnspecifiedInverter,
)
from ._meter import Meter
from ._operational_mode import ElectricalComponentOperationalMode
from ._power_transformer import PowerTransformer
from ._precharger import Precharger
from ._problematic import (
Expand Down Expand Up @@ -79,6 +80,7 @@
"ElectricalComponentConnection",
"ElectricalComponentDiagnosticCode",
"ElectricalComponentId",
"ElectricalComponentOperationalMode",
"ElectricalComponentStateCode",
"ElectricalComponentTypes",
"Electrolyzer",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .. import MicrogridId
from ._category import ElectricalComponentCategory
from ._ids import ElectricalComponentId
from ._operational_mode import ElectricalComponentOperationalMode


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

manufacturer: str | None = None
"""The manufacturer of this electrical component."""
model: str | None = None
"""The model of this electrical component.

model_name: str | None = None
"""The model name of this electrical component."""
This includes both the manufacturer and the model name.
"""

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

rated_bounds: Mapping[Metric | int, Bounds] = dataclasses.field(
operational_mode: ElectricalComponentOperationalMode | int = (
ElectricalComponentOperationalMode.UNSPECIFIED
)
"""The operational mode of this electrical component.

This indicates whether the component is active and operational, and whether it
provides telemetry data, accepts control commands, or both.
"""

metric_config_bounds: Mapping[Metric | int, Bounds] = dataclasses.field(
default_factory=dict,
# dict is not hashable, so we don't use this field to calculate the hash. This
# shouldn't be a problem since it is very unlikely that two components with all
# other attributes being equal would have different category specific metadata,
# so hash collisions should be still very unlikely.
hash=False,
)
"""List of rated bounds present for the electrical component identified by Metric."""
"""The metric configuration bounds for this electrical component, keyed by metric.

These bounds may be derived from the component configuration, manufacturer
limits, or limits of other devices.
"""

category_specific_metadata: Mapping[str, Any] = dataclasses.field(
default_factory=dict,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# License: MIT
# Copyright © 2026 Frequenz Energy-as-a-Service GmbH

"""Electrical component operational modes."""

import enum


@enum.unique
class ElectricalComponentOperationalMode(enum.Enum):
"""The operational mode of an electrical component.

This indicates whether the component is active and operational, and whether it
provides telemetry data, accepts control commands, or both.
"""

UNSPECIFIED = 0
"""Default value when the operational mode is not explicitly set."""

INACTIVE = 1
"""The component is inactive and not operational.

It does not provide telemetry data, and it does not accept control commands.
"""

TELEMETRY_ONLY = 2
"""The component is active and operational, providing telemetry data only.

It does not accept control commands.
"""

CONTROL_ONLY = 3
"""The component is active and operational, accepting control commands only.

It does not provide telemetry data.
"""

CONTROL_AND_TELEMETRY = 4
"""The component is active and operational.

It provides telemetry data and accepts control commands.
"""
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
electrical_component_connection_from_proto,
electrical_component_connection_from_proto_with_issues,
)
from ._operational_mode import (
electrical_component_operational_mode_from_proto,
electrical_component_operational_mode_to_proto,
)
from ._state_code import (
electrical_component_state_code_from_proto,
electrical_component_state_code_to_proto,
Expand All @@ -33,6 +37,8 @@
"electrical_component_diagnostic_code_to_proto",
"electrical_component_from_proto",
"electrical_component_from_proto_with_issues",
"electrical_component_operational_mode_from_proto",
"electrical_component_operational_mode_to_proto",
"electrical_component_state_code_from_proto",
"electrical_component_state_code_to_proto",
]
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
DcEvCharger,
ElectricalComponentCategory,
ElectricalComponentId,
ElectricalComponentOperationalMode,
ElectricalComponentTypes,
Electrolyzer,
EvChargerType,
Expand All @@ -55,6 +56,7 @@
UnspecifiedInverter,
WindTurbine,
)
from ._operational_mode import electrical_component_operational_mode_from_proto

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -104,15 +106,16 @@ class _ElectricalComponentBaseData(NamedTuple):
component_id: ElectricalComponentId
microgrid_id: MicrogridId
name: str | None
manufacturer: str | None
model_name: str | None
model: str | None
category: ElectricalComponentCategory | int
lifetime: Lifetime
rated_bounds: dict[Metric | int, Bounds]
metric_config_bounds: dict[Metric | int, Bounds]
category_specific_info: dict[str, Any]
operational_mode: ElectricalComponentOperationalMode | int
category_mismatched: bool = False


# pylint: disable-next=too-many-locals
def _electrical_component_base_from_proto_with_issues(
message: electrical_components_pb2.ElectricalComponent,
*,
Expand All @@ -136,19 +139,19 @@ def _electrical_component_base_from_proto_with_issues(
if name is None:
minor_issues.append("name is empty")

manufacturer = message.manufacturer or None
if manufacturer is None:
minor_issues.append("manufacturer is empty")
model = message.model or None
if model is None:
minor_issues.append("model is empty")

model_name = message.model_name or None
if model_name is None:
minor_issues.append("model_name is empty")
operational_mode = electrical_component_operational_mode_from_proto(
message.operational_mode
)

lifetime = _get_operational_lifetime_from_proto(
message, major_issues=major_issues, minor_issues=minor_issues
)

rated_bounds = _metric_config_bounds_from_proto(
metric_config_bounds = _metric_config_bounds_from_proto(
message.metric_config_bounds,
major_issues=major_issues,
minor_issues=minor_issues,
Expand Down Expand Up @@ -184,12 +187,12 @@ def _electrical_component_base_from_proto_with_issues(
component_id,
microgrid_id,
name,
manufacturer,
model_name,
model,
category,
lifetime,
rated_bounds,
metric_config_bounds,
category_specific_info,
operational_mode,
category_mismatched,
)

Expand Down Expand Up @@ -220,12 +223,12 @@ def electrical_component_from_proto_with_issues(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
category=base_data.category,
operational_lifetime=base_data.lifetime,
operational_mode=base_data.operational_mode,
category_specific_metadata=base_data.category_specific_info,
rated_bounds=base_data.rated_bounds,
metric_config_bounds=base_data.metric_config_bounds,
)

match base_data.category:
Expand All @@ -234,11 +237,11 @@ def electrical_component_from_proto_with_issues(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
category=base_data.category,
operational_lifetime=base_data.lifetime,
rated_bounds=base_data.rated_bounds,
operational_mode=base_data.operational_mode,
metric_config_bounds=base_data.metric_config_bounds,
)
case (
ElectricalComponentCategory.UNSPECIFIED
Expand All @@ -257,10 +260,10 @@ def electrical_component_from_proto_with_issues(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
operational_lifetime=base_data.lifetime,
rated_bounds=base_data.rated_bounds,
operational_mode=base_data.operational_mode,
metric_config_bounds=base_data.metric_config_bounds,
)
case ElectricalComponentCategory.BATTERY:
battery_enum_to_class: dict[
Expand All @@ -281,21 +284,21 @@ def electrical_component_from_proto_with_issues(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
operational_lifetime=base_data.lifetime,
rated_bounds=base_data.rated_bounds,
operational_mode=base_data.operational_mode,
metric_config_bounds=base_data.metric_config_bounds,
)
case int():
major_issues.append(f"battery type {battery_type} is unrecognized")
return UnrecognizedBattery(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
operational_lifetime=base_data.lifetime,
rated_bounds=base_data.rated_bounds,
operational_mode=base_data.operational_mode,
metric_config_bounds=base_data.metric_config_bounds,
type=battery_type,
)
case unexpected_battery_type:
Expand Down Expand Up @@ -328,10 +331,10 @@ def electrical_component_from_proto_with_issues(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
operational_lifetime=base_data.lifetime,
rated_bounds=base_data.rated_bounds,
operational_mode=base_data.operational_mode,
metric_config_bounds=base_data.metric_config_bounds,
)
case int():
major_issues.append(
Expand All @@ -341,10 +344,10 @@ def electrical_component_from_proto_with_issues(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
operational_lifetime=base_data.lifetime,
rated_bounds=base_data.rated_bounds,
operational_mode=base_data.operational_mode,
metric_config_bounds=base_data.metric_config_bounds,
type=ev_charger_type,
)
case unexpected_ev_charger_type:
Expand All @@ -358,10 +361,10 @@ def electrical_component_from_proto_with_issues(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
operational_lifetime=base_data.lifetime,
rated_bounds=base_data.rated_bounds,
operational_mode=base_data.operational_mode,
metric_config_bounds=base_data.metric_config_bounds,
rated_fuse_current=rated_fuse_current,
)
case ElectricalComponentCategory.INVERTER:
Expand Down Expand Up @@ -392,10 +395,10 @@ def electrical_component_from_proto_with_issues(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
operational_lifetime=base_data.lifetime,
rated_bounds=base_data.rated_bounds,
operational_mode=base_data.operational_mode,
metric_config_bounds=base_data.metric_config_bounds,
)
case int():
major_issues.append(
Expand All @@ -405,10 +408,10 @@ def electrical_component_from_proto_with_issues(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
operational_lifetime=base_data.lifetime,
rated_bounds=base_data.rated_bounds,
operational_mode=base_data.operational_mode,
metric_config_bounds=base_data.metric_config_bounds,
type=inverter_type,
)
case unexpected_inverter_type:
Expand All @@ -418,10 +421,10 @@ def electrical_component_from_proto_with_issues(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
operational_lifetime=base_data.lifetime,
rated_bounds=base_data.rated_bounds,
operational_mode=base_data.operational_mode,
metric_config_bounds=base_data.metric_config_bounds,
primary_voltage=message.category_specific_info.power_transformer.primary,
secondary_voltage=message.category_specific_info.power_transformer.secondary,
)
Expand All @@ -439,11 +442,11 @@ def electrical_component_from_proto_with_issues(
id=base_data.component_id,
microgrid_id=base_data.microgrid_id,
name=base_data.name,
manufacturer=base_data.manufacturer,
model_name=base_data.model_name,
model=base_data.model,
category=base_data.category.value,
operational_lifetime=base_data.lifetime,
rated_bounds=base_data.rated_bounds,
operational_mode=base_data.operational_mode,
metric_config_bounds=base_data.metric_config_bounds,
)
case unexpected_category:
assert_never(unexpected_category)
Expand Down
Loading
Loading