Skip to content

Commit 5b8fd4c

Browse files
Add metadata to Components
This commit introduces metadata to Components by incorporating GridMetadata, which now includes the maximum grid current, specified in Amperes. Signed-off-by: Tiyash Basu <[email protected]>
1 parent eebced4 commit 5b8fd4c

File tree

5 files changed

+79
-6
lines changed

5 files changed

+79
-6
lines changed

src/frequenz/sdk/microgrid/client/_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
)
3737
from ..component._component import (
3838
_component_category_from_protobuf,
39+
_component_metadata_from_protobuf,
3940
_component_type_from_protobuf,
4041
)
4142
from ._connection import Connection
@@ -254,6 +255,7 @@ async def components(self) -> Iterable[Component]:
254255
c.id,
255256
_component_category_from_protobuf(c.category),
256257
_component_type_from_protobuf(c.category, c.inverter),
258+
_component_metadata_from_protobuf(c.category, c.grid),
257259
),
258260
components_only,
259261
)

src/frequenz/sdk/microgrid/component/__init__.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@
66
This package provides classes to operate con microgrid components.
77
"""
88

9-
from ._component import Component, ComponentCategory, ComponentMetricId, InverterType
9+
from ._component import (
10+
Component,
11+
ComponentCategory,
12+
ComponentMetadata,
13+
ComponentMetricId,
14+
GridMetadata,
15+
InverterType,
16+
)
1017
from ._component_data import (
1118
BatteryData,
1219
ComponentData,
@@ -21,10 +28,12 @@
2128
"Component",
2229
"ComponentData",
2330
"ComponentCategory",
31+
"ComponentMetadata",
2432
"ComponentMetricId",
2533
"EVChargerCableState",
2634
"EVChargerComponentState",
2735
"EVChargerData",
36+
"GridMetadata",
2837
"InverterData",
2938
"InverterType",
3039
"MeterData",

src/frequenz/sdk/microgrid/component/_component.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55

66
from __future__ import annotations
77

8+
import typing
89
from dataclasses import dataclass
910
from enum import Enum
1011
from typing import Optional
1112

1213
import frequenz.api.common.components_pb2 as components_pb
14+
import frequenz.api.microgrid.grid_pb2 as grid_pb
1315
import frequenz.api.microgrid.inverter_pb2 as inverter_pb
1416

1517

@@ -106,13 +108,64 @@ def _component_category_from_protobuf(
106108
return ComponentCategory(component_category)
107109

108110

111+
class ComponentMetadata:
112+
"""Base class for component metadata classes."""
113+
114+
115+
class GridMetadata(ComponentMetadata):
116+
"""Metadata for a grid connection point."""
117+
118+
def __init__(self, max_current: float) -> None:
119+
"""Create a new instance.
120+
121+
Args:
122+
max_current: maximum current rating of the grid connection point in amps.
123+
"""
124+
self._max_current = max_current
125+
126+
@property
127+
def max_current(self) -> float:
128+
"""Get the maximum current rating of the grid connection point.
129+
130+
Returns:
131+
Maximum current rating in amps.
132+
"""
133+
return self._max_current
134+
135+
def __eq__(self, other: typing.Any) -> bool:
136+
"""Check if this instance is equal to another.
137+
138+
Args:
139+
other: object to compare to.
140+
141+
Returns:
142+
`True` if `other` is a `GridMetadata` instance and `max_current` is equal,
143+
`False` otherwise.
144+
"""
145+
if not isinstance(other, GridMetadata):
146+
return False
147+
148+
return self.max_current == other.max_current
149+
150+
151+
def _component_metadata_from_protobuf(
152+
component_category: components_pb.ComponentCategory.ValueType,
153+
component_metadata: grid_pb.Metadata,
154+
) -> Optional[GridMetadata]:
155+
if component_category == components_pb.ComponentCategory.COMPONENT_CATEGORY_GRID:
156+
return GridMetadata(float(component_metadata.rated_fuse_current))
157+
158+
return None
159+
160+
109161
@dataclass(frozen=True)
110162
class Component:
111163
"""Metadata for a single microgrid component."""
112164

113165
component_id: int
114166
category: ComponentCategory
115167
type: Optional[ComponentType] = None
168+
metadata: Optional[ComponentMetadata] = None
116169

117170
def is_valid(self) -> bool:
118171
"""Check if this instance contains valid data.

tests/microgrid/test_client.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
Component,
2222
ComponentCategory,
2323
EVChargerData,
24+
GridMetadata,
2425
InverterData,
2526
InverterType,
2627
MeterData,
@@ -117,9 +118,12 @@ async def test_components(self) -> None:
117118
(999, components_pb.ComponentCategory.COMPONENT_CATEGORY_SENSOR),
118119
]
119120
)
121+
120122
assert set(await microgrid.components()) == {
121123
Component(100, ComponentCategory.NONE),
122-
Component(101, ComponentCategory.GRID),
124+
Component(
125+
101, ComponentCategory.GRID, None, GridMetadata(max_current=0.0)
126+
),
123127
Component(104, ComponentCategory.METER),
124128
Component(105, ComponentCategory.INVERTER, InverterType.NONE),
125129
Component(106, ComponentCategory.BATTERY),

tests/microgrid/test_graph.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@
1818

1919
import frequenz.sdk.microgrid._graph as gr
2020
from frequenz.sdk.microgrid.client import Connection, MicrogridGrpcClient
21-
from frequenz.sdk.microgrid.component import Component, ComponentCategory, InverterType
21+
from frequenz.sdk.microgrid.component import (
22+
Component,
23+
ComponentCategory,
24+
GridMetadata,
25+
InverterType,
26+
)
2227

2328
from .mock_api import MockGrpcServer, MockMicrogridServicer
2429

@@ -817,7 +822,7 @@ async def test_refresh_from_api(self) -> None:
817822
await graph.refresh_from_api(client)
818823

819824
expected = {
820-
Component(101, ComponentCategory.GRID),
825+
Component(101, ComponentCategory.GRID, None, GridMetadata(max_current=0.0)),
821826
Component(111, ComponentCategory.METER),
822827
Component(131, ComponentCategory.EV_CHARGER),
823828
}
@@ -843,7 +848,7 @@ async def test_refresh_from_api(self) -> None:
843848
servicer.set_connections([(707, 717), (717, 727), (727, 737), (717, 747)])
844849
await graph.refresh_from_api(client)
845850
expected = {
846-
Component(707, ComponentCategory.GRID),
851+
Component(707, ComponentCategory.GRID, None, GridMetadata(max_current=0.0)),
847852
Component(717, ComponentCategory.METER),
848853
Component(727, ComponentCategory.INVERTER, InverterType.NONE),
849854
Component(737, ComponentCategory.BATTERY),
@@ -1146,7 +1151,7 @@ def test__validate_grid_endpoint(self) -> None:
11461151
gr.InvalidGraphError,
11471152
match=r"Grid endpoint 1 has graph predecessors: \[Component"
11481153
r"\(component_id=99, category=<ComponentCategory.METER: 2>, "
1149-
r"type=None\)\]",
1154+
r"type=None, metadata=None\)\]",
11501155
) as _err_predecessors:
11511156
graph._validate_grid_endpoint()
11521157

0 commit comments

Comments
 (0)