Skip to content
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

Document class and module attributes #618

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
5 changes: 5 additions & 0 deletions src/frequenz/sdk/_api_client/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ class ApiProtocol(Enum):
"""Enumerated values of supported API types."""

GRPC = 1
"""gRPC API."""

REST = 2
"""REST API."""

FILESYSTEM = 3
"""Filesystem API."""


class ApiClient(ABC):
Expand Down
2 changes: 1 addition & 1 deletion src/frequenz/sdk/_internal/_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async def cancel_and_await(task: asyncio.Task[Any]) -> None:

Exits immediately if the task is already done.

The `CancelledError` is suppresed, but any other exception will be propagated.
The `CancelledError` is suppressed, but any other exception will be propagated.

Args:
task: The task to be cancelled and waited for.
Expand Down
7 changes: 7 additions & 0 deletions src/frequenz/sdk/_internal/_singleton_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,14 @@ class SingletonMeta(type):
"""This is a thread-safe implementation of Singleton."""

_instances: Dict[Any, type] = {}
"""The dictionary of instances of the singleton classes."""

_lock: Lock = Lock()
"""The lock to ensure thread safety.

The lock to acquire when creating a singleton instance, preventing
multiple threads from creating instances simultaneously.
"""

def __call__(cls, *args: Any, **kwargs: Any) -> type:
"""Overload function call operator to return the singleton instance.
Expand Down
9 changes: 7 additions & 2 deletions src/frequenz/sdk/actor/_data_sourcing/microgrid_api_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,13 @@ def __init__(
instance.
"""
self._comp_categories_cache: Dict[int, ComponentCategory] = {}

self.comp_data_receivers: Dict[int, Receiver[Any]] = {}
"""The dictionary of component IDs to data receivers."""

self.comp_data_tasks: Dict[int, asyncio.Task[None]] = {}
"""The dictionary of component IDs to asyncio tasks."""

self._registry = registry
self._req_streaming_metrics: Dict[
int, Dict[ComponentMetricId, List[ComponentMetricRequest]]
Expand Down Expand Up @@ -343,10 +348,10 @@ def _get_metric_senders(
self._get_data_extraction_method(category, metric),
[
self._registry.new_sender(request.get_channel_name())
for request in reqlist
for request in req_list
],
)
for (metric, reqlist) in requests.items()
for (metric, req_list) in requests.items()
]

async def _handle_data_stream(
Expand Down
2 changes: 1 addition & 1 deletion src/frequenz/sdk/actor/_resampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__( # pylint: disable=too-many-arguments
the [`DataSourcingActor`][frequenz.sdk.actor.DataSourcingActor]
to subscribe to component metrics.
resampling_request_receiver: The receiver to use to receive new
resampmling subscription requests.
resampling subscription requests.
config: The configuration for the resampler.
name: The name of the actor. If `None`, `str(id(self))` will be used. This
is used mostly for debugging purposes.
Expand Down
32 changes: 25 additions & 7 deletions src/frequenz/sdk/actor/power_distributing/_battery_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from enum import Enum
from typing import Iterable, Optional, Set, TypeVar, Union
from typing import Iterable, Optional, Set, Union

# pylint: disable=no-name-in-module
from frequenz.api.microgrid.battery_pb2 import ComponentState as BatteryComponentState
Expand Down Expand Up @@ -60,9 +60,6 @@ class SetPowerResult:
"""Set of the batteries that failed."""


T = TypeVar("T")


@dataclass
class _ComponentStreamStatus:
component_id: int
Expand All @@ -81,15 +78,23 @@ class _ComponentStreamStatus:
@dataclass
class _BlockingStatus:
min_duration_sec: float
"""The minimum blocking duration (in seconds)."""

max_duration_sec: float
"""The maximum blocking duration (in seconds)."""

last_blocking_duration_sec: float = 0.0
"""Last blocking duration (in seconds)."""

blocked_until: datetime | None = None
"""Until when the battery is blocked."""

def __post_init__(self) -> None:
assert self.min_duration_sec <= self.max_duration_sec, (
f"Minimum blocking duration ({self.min_duration_sec}) cannot be greater "
f"than maximum blocking duration ({self.max_duration_sec})"
)
self.last_blocking_duration_sec: float = self.min_duration_sec
self.blocked_until: Optional[datetime] = None
self.last_blocking_duration_sec = self.min_duration_sec

def block(self) -> float:
"""Block battery.
Expand Down Expand Up @@ -150,21 +155,34 @@ class BatteryStatusTracker:
Status updates are sent out only when there is a status change.
"""

# Class attributes
_battery_valid_relay: Set[BatteryRelayState.ValueType] = {
BatteryRelayState.RELAY_STATE_CLOSED
}
"""The list of valid relay states of a battery.

A working battery in any other battery relay state will be reported as failing.
"""

_battery_valid_state: Set[BatteryComponentState.ValueType] = {
BatteryComponentState.COMPONENT_STATE_IDLE,
BatteryComponentState.COMPONENT_STATE_CHARGING,
BatteryComponentState.COMPONENT_STATE_DISCHARGING,
}
"""The list of valid states of a battery.

A working battery in any other battery state will be reported as failing.
"""

_inverter_valid_state: Set[InverterComponentState.ValueType] = {
InverterComponentState.COMPONENT_STATE_STANDBY,
InverterComponentState.COMPONENT_STATE_IDLE,
InverterComponentState.COMPONENT_STATE_CHARGING,
InverterComponentState.COMPONENT_STATE_DISCHARGING,
}
"""The list of valid states of an inverter.

A working inverter in any other inverter state will be reported as failing.
"""

def __init__( # pylint: disable=too-many-arguments
self,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,16 @@ def __init__(

# NOTE: power_distributor_exponent should be received from ConfigManager
self.power_distributor_exponent: float = 1.0
"""The exponent for the power distribution algorithm.

The exponent determines how fast the batteries should strive to the
equal SoC level.
"""

self.distribution_algorithm = DistributionAlgorithm(
self.power_distributor_exponent
)
"""The distribution algorithm used to distribute power between batteries."""

self._bat_inv_map, self._inv_bat_map = self._get_components_pairs(
connection_manager.get().component_graph
Expand Down
7 changes: 7 additions & 0 deletions src/frequenz/sdk/actor/power_distributing/result.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,16 @@ class PowerBounds:
"""Inclusion and exclusion power bounds for requested batteries."""

inclusion_lower: float
"""The lower value of the inclusion power bounds for the requested batteries."""

exclusion_lower: float
"""The lower value of the exclusion power bounds for the requested batteries."""

exclusion_upper: float
"""The upper value of the exclusion power bounds for the requested batteries."""

inclusion_upper: float
"""The upper value of the inclusion power bounds for the requested batteries."""


@dataclasses.dataclass
Expand Down
1 change: 1 addition & 0 deletions src/frequenz/sdk/config/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
_logger = logging.getLogger(__name__)

T = TypeVar("T")
"""Type variable for validating configuration values."""


class Config:
Expand Down
4 changes: 4 additions & 0 deletions src/frequenz/sdk/microgrid/_data_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,18 @@
"""

_T = typing.TypeVar("_T")
"""Type variable for generic actor types."""


@dataclass
class _ActorInfo(typing.Generic[_T]):
"""Holds instances of core data pipeline actors and their request channels."""

actor: _T
"""The actor instance."""

channel: Broadcast["ComponentMetricRequest"]
"""The request channel for the actor."""


class _DataPipeline:
Expand Down
5 changes: 2 additions & 3 deletions src/frequenz/sdk/microgrid/_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ def dfs(
"""
Search for components that fulfill the condition in the Graph.

DFS is used for searching the graph. The graph travarsal is stopped
DFS is used for searching the graph. The graph traversal is stopped
once a component fulfills the condition.

Args:
Expand All @@ -730,7 +730,7 @@ def dfs(
condition: The condition function to check for.

Returns:
A set of component ids where the coresponding components fulfill
A set of component ids where the corresponding components fulfill
the condition function.
"""
if current_node in visited:
Expand Down Expand Up @@ -896,7 +896,6 @@ def _validate_leaf_components(self) -> None:
component_category={
ComponentCategory.BATTERY,
ComponentCategory.EV_CHARGER,
ComponentCategory.PV_ARRAY,
}
)
)
Expand Down
7 changes: 6 additions & 1 deletion src/frequenz/sdk/microgrid/client/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
from ._connection import Connection
from ._retry import LinearBackoff, RetryStrategy

# Default timeout applied to all gRPC calls
DEFAULT_GRPC_CALL_TIMEOUT = 60.0
"""The default timeout for gRPC calls made by this client (in seconds)."""

# A generic type for representing various component data types, used in the
# generic function `MicrogridGrpcClient._component_data_task` that fetches
Expand All @@ -55,6 +55,7 @@
InverterData,
EVChargerData,
)
"""Type variable for representing various component data types."""

_logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -215,7 +216,11 @@ def __init__(
method gets lost.
"""
self.target = target
"""The location (as "host:port") of the microgrid API gRPC server."""

self.api = MicrogridStub(grpc_channel)
"""The gRPC stub for the microgrid API."""

self._component_streams: Dict[int, Broadcast[Any]] = {}
self._streaming_tasks: Dict[int, asyncio.Task[None]] = {}
self._retry_spec = retry_spec
Expand Down
3 changes: 3 additions & 0 deletions src/frequenz/sdk/microgrid/client/_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ class Connection(NamedTuple):
"""Metadata for a connection between microgrid components."""

start: int
"""The component ID that represents the start component of the connection."""

end: int
"""The component ID that represents the end component of the connection."""

def is_valid(self) -> bool:
"""Check if this instance contains valid data.
Expand Down
8 changes: 8 additions & 0 deletions src/frequenz/sdk/microgrid/client/_retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
from typing import Iterator, Optional

DEFAULT_RETRY_INTERVAL = 3.0
"""Default retry interval, in seconds."""

DEFAULT_RETRY_JITTER = 1.0
"""Default retry jitter, in seconds."""


class RetryStrategy(ABC):
Expand Down Expand Up @@ -114,8 +117,13 @@ class ExponentialBackoff(RetryStrategy):
"""Provides methods for calculating the exponential interval between retries."""

DEFAULT_INTERVAL = DEFAULT_RETRY_INTERVAL
"""Default retry interval, in seconds."""

DEFAULT_MAX_INTERVAL = 60.0
"""Default maximum retry interval, in seconds."""

DEFAULT_MULTIPLIER = 2.0
"""Default multiplier for exponential increment."""

# pylint: disable=too-many-arguments
def __init__(
Expand Down
Loading