-
Notifications
You must be signed in to change notification settings - Fork 5
Add gRPC calls to control components #158
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds gRPC calls for controlling component power and bounds with new methods in the microgrid client, along with reorganized metrics and component definitions. Key changes include:
- Implementation of set_component_power_active, set_component_power_reactive, and add_component_bounds methods in the client.
- Addition of new component base class and improved metrics package structure.
- Comprehensive test cases for various power and bounds scenarios.
Reviewed Changes
Copilot reviewed 37 out of 37 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/client_test_cases/set_component_power_active/* | Test cases for handling invalid power and lifetime parameters |
| tests/client_test_cases/add_component_bounds/* | Test cases validating bounds addition behavior |
| src/frequenz/client/microgrid/metrics/_bounds_proto.py | Added bounds conversion from protobuf messages |
| src/frequenz/client/microgrid/metrics/_bounds.py | Added Bounds dataclass with validation logic |
| src/frequenz/client/microgrid/_client.py | New gRPC methods for setting power and adding bounds, along with argument validation |
| src/frequenz/client/microgrid/component/* | New base component and associated categorization and status definitions |
93ee4c4 to
d069f10
Compare
d069f10 to
19932f5
Compare
tests/metrics/test_bounds.py
Outdated
| def test_creation() -> None: | ||
| """Test creation of Bounds with valid values.""" | ||
| bounds = Bounds(lower=-10.0, upper=10.0) | ||
| assert bounds.lower == -10.0 | ||
| assert bounds.upper == 10.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe also a case where lower and upper are the same values?
| def _delta_to_seconds(delta: timedelta | None) -> int | None: | ||
| """Convert a `timedelta` to seconds (or `None` if `None`).""" | ||
| return round(delta.total_seconds()) if delta is not None else None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This returns float | None, not int | None
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. Interesting that mypy doesn't catch this 🤔
.... looking 🔍 ......
It is not reported because it does return int. It seems to be undocumented, but it actually returns int if ndigits is not specified:
class _SupportsRound1(Protocol[_T_co]):
def __round__(self) -> _T_co: ...
class _SupportsRound2(Protocol[_T_co]):
def __round__(self, ndigits: int, /) -> _T_co: ...
@overload
def round(number: _SupportsRound1[_T], ndigits: None = None) -> _T: ...
@overload
def round(number: _SupportsRound2[_T], ndigits: SupportsIndex) -> _T: ...and for float:
@overload
def __round__(self, ndigits: None = None, /) -> int: ...
@overload
def __round__(self, ndigits: SupportsIndex, /) -> float: ...💥
tests/client_test_cases/set_component_power_active/no_lifetime_case.py
Outdated
Show resolved
Hide resolved
tests/client_test_cases/set_component_power_active/success_case.py
Outdated
Show resolved
Hide resolved
| If multiple inclusion bounds have been provided for a metric, then the | ||
| overlapping bounds are merged into a single bound, and non-overlapping | ||
| bounds are kept separate. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API also specifies that there can't be more than two non-overlapping bounds, right? In that case, it would be nice if the docs for the add_component_bounds method specify that users should be careful not to add more than two non-overlapping bounds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what do you mean by this? It says explicitly there that overlapping bounds will be merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have this vague memory that these were supposed to exactly replace inclusion/exclusion bounds, so there will not be more than two non-overlapping bounds in the new scheme, and that that was going to be codified in the docs.
But that doesn't appear to be the case, so nvm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh, yeah, I remember that too, not sure what's the current state of this, if that limitation still holds or have been lifted. Since one can add bounds, I guess it is not true anymore. @tiyash-basu-frequenz ?
Signed-off-by: Leandro Lucarella <[email protected]>
Signed-off-by: Leandro Lucarella <[email protected]>
Arguments of `pytest`s are pretty self descriptive, as they come from figures, parametrization or other decorators. Signed-off-by: Leandro Lucarella <[email protected]>
We will need to add more metrics-related wrappers in the future, so it is better to make it a package. Signed-off-by: Leandro Lucarella <[email protected]>
Signed-off-by: Leandro Lucarella <[email protected]>
Signed-off-by: Leandro Lucarella <[email protected]>
Signed-off-by: Leandro Lucarella <[email protected]>
This class will be used to combine classic inheritance (all components will derive from it), a mix-in to provide common functionality of all components, and also build "algebraic data types", so they can be used in `match` statements and easily do exhaustion checks to make sure all possible types of components are handled when needed. Note it doesn't make sense to make this a `abc.ABC` because there are no abstract methods. Signed-off-by: Leandro Lucarella <[email protected]>
These 2 calls are identically, they just affect active or reactive power
respectively, so there are a few utility function that are shared, as
well as test cases.
For test cases we need to use a couple of hacks to share them.
* `*_case.py` files for `set_reactive_power` are symlinks to
`set_active_power()`
* Both have their own `_config.py` file where we define the classes used
for the tests, so we can share them, as only the request/response
objects change between both.
* Because of the client test framework works, `_config` must be imported
as a top-level module.
* We need to ignore `pylint` and `mypy` checks when importing, as they
can't find the `_config` module at the top-level.
Signed-off-by: Leandro Lucarella <[email protected]>
Signed-off-by: Leandro Lucarella <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to fix all comments of stuff that needed fixing. I added more cases to the bounds creation tests.
| If multiple inclusion bounds have been provided for a metric, then the | ||
| overlapping bounds are merged into a single bound, and non-overlapping | ||
| bounds are kept separate. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what do you mean by this? It says explicitly there that overlapping bounds will be merged.
| def _delta_to_seconds(delta: timedelta | None) -> int | None: | ||
| """Convert a `timedelta` to seconds (or `None` if `None`).""" | ||
| return round(delta.total_seconds()) if delta is not None else None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True. Interesting that mypy doesn't catch this 🤔
.... looking 🔍 ......
It is not reported because it does return int. It seems to be undocumented, but it actually returns int if ndigits is not specified:
class _SupportsRound1(Protocol[_T_co]):
def __round__(self) -> _T_co: ...
class _SupportsRound2(Protocol[_T_co]):
def __round__(self, ndigits: int, /) -> _T_co: ...
@overload
def round(number: _SupportsRound1[_T], ndigits: None = None) -> _T: ...
@overload
def round(number: _SupportsRound2[_T], ndigits: SupportsIndex) -> _T: ...and for float:
@overload
def __round__(self, ndigits: None = None, /) -> int: ...
@overload
def __round__(self, ndigits: SupportsIndex, /) -> float: ...💥
|
Also added a new commit to fix the |
|
Enabling auto-merge. |
Not sure I see it. |
|
I see, you meant the typo. The description sounded like something more significant. 🤔 |
Functionality for managing component operational limits has been implemented with
AddComponentBounds, supported by a newBoundswrapper.The client now supports setting active and reactive power for components through the
SetComponentPowerActiveandSetComponentPowerReactivemethods. These methods share a common underlying implementation and test suite, with minor adaptations for the specific power type being controlled.A new base
Componentclass has been added, which will serve as a common ancestor for all component types. This facilitates shared functionality and enables the use of algebraic data types for more robustmatchstatements and exhaustiveness checks. Sub-classes will be added in follow-up pull requests.The
metricsmodule has been restructured into a package to better organize upcoming additions. AMetricenum has been introduced, and support for loadingAggregatedMetricValueandLifetimedata from protobuf messages has been added.Part of #55.