|  | 
| 2 | 2 | 
 | 
| 3 | 3 | ## Summary | 
| 4 | 4 | 
 | 
| 5 |  | -This release replaces the `@actor` decorator with a new `Actor` class. | 
|  | 5 | +<!-- Here goes a general summary of what this release is about --> | 
| 6 | 6 | 
 | 
| 7 | 7 | ## Upgrading | 
| 8 | 8 | 
 | 
| 9 |  | - | 
| 10 |  | -- The `frequenz.sdk.power` package contained the power distribution algorithm, which is for internal use in the sdk, and is no longer part of the public API. | 
| 11 |  | - | 
| 12 |  | -- `PowerDistributingActor`'s result type `OutOfBound` has been renamed to `OutOfBounds`, and its member variable `bound` has been renamed to `bounds`. | 
| 13 |  | - | 
| 14 |  | -- The `@actor` decorator was replaced by the new `Actor` class. The main differences between the new class and the old decorator are: | 
| 15 |  | - | 
| 16 |  | -  * It doesn't start automatically, `start()` needs to be called to start an actor (using the `frequenz.sdk.actor.run()` function is recommended). | 
| 17 |  | -  * The method to implement the main logic was renamed from `run()` to `_run()`, as it is not intended to be run externally. | 
| 18 |  | -  * Actors can have an optional `name` (useful for debugging/logging purposes). | 
| 19 |  | -  * The actor will only be restarted if an unhandled `Exception` is raised by `_run()`. It will not be restarted if the `_run()` method finishes normally. If an unhandled `BaseException` is raised instead, it will be re-raised. For normal cancellation the `_run()` method should handle `asyncio.CancelledError` if the cancellation shouldn't be propagated (this is the same as with the decorator). | 
| 20 |  | -  * The `_stop()` method is public (`stop()`) and will `cancel()` and `await` for the task to finish, catching the `asyncio.CancelledError`. | 
| 21 |  | -  * The `join()` method is renamed to `wait()`, but they can also be awaited directly ( `await actor`). | 
| 22 |  | -  * For deterministic cleanup, actors can now be used as `async` context managers. | 
| 23 |  | - | 
| 24 |  | -  Most actors can be migrated following these steps: | 
| 25 |  | - | 
| 26 |  | -  1. Remove the decorator | 
| 27 |  | -  2. Add `Actor` as a base class | 
| 28 |  | -  3. Rename `run()` to `_run()` | 
| 29 |  | -  4. Forward the `name` argument (optional but recommended) | 
| 30 |  | - | 
| 31 |  | -  For example, this old actor: | 
| 32 |  | - | 
| 33 |  | -  ```python | 
| 34 |  | -  from frequenz.sdk.actor import actor | 
| 35 |  | - | 
| 36 |  | -  @actor | 
| 37 |  | -  class TheActor: | 
| 38 |  | -      def __init__(self, actor_args) -> None: | 
| 39 |  | -          # init code | 
| 40 |  | - | 
| 41 |  | -      def run(self) -> None: | 
| 42 |  | -          # run code | 
| 43 |  | -  ``` | 
| 44 |  | - | 
| 45 |  | -  Can be migrated as: | 
| 46 |  | - | 
| 47 |  | -  ```python | 
| 48 |  | -  import asyncio | 
| 49 |  | -  from frequenz.sdk.actor import Actor | 
| 50 |  | - | 
| 51 |  | -  class TheActor(Actor): | 
| 52 |  | -      def __init__(self, actor_args, | 
| 53 |  | -          *, | 
| 54 |  | -          name: str | None = None, | 
| 55 |  | -      ) -> None: | 
| 56 |  | -          super().__init__(name=name) | 
| 57 |  | -          # init code | 
| 58 |  | - | 
| 59 |  | -      def _run(self) -> None: | 
| 60 |  | -          # run code | 
| 61 |  | -  ``` | 
| 62 |  | - | 
| 63 |  | -  Then you can instantiate all your actors first and then run them using: | 
| 64 |  | - | 
| 65 |  | -  ```python | 
| 66 |  | -  from frequenz.sdk.actor import run | 
| 67 |  | -  # Init code | 
| 68 |  | -  actor = TheActor() | 
| 69 |  | -  other_actor = OtherActor() | 
| 70 |  | -  # more setup | 
| 71 |  | -  await run(actor, other_actor)  # Start and await for all the actors | 
| 72 |  | -  ``` | 
| 73 |  | - | 
| 74 |  | -- The `MovingWindow` is now a `BackgroundService`, so it needs to be started manually with `window.start()`. It is recommended to use it as an `async` context manager if possible though: | 
| 75 |  | - | 
| 76 |  | -    ```python | 
| 77 |  | -    async with MovingWindow(...) as window: | 
| 78 |  | -        # The moving windows is started here | 
| 79 |  | -        use(window) | 
| 80 |  | -    # The moving window is stopped here | 
| 81 |  | -    ``` | 
| 82 |  | - | 
| 83 |  | -- The base actors (`ConfigManagingActor`, `ComponentMetricsResamplingActor`, `DataSourcingActor`, `PowerDistributingActor`) now inherit from the new `Actor` class, if you are using them directly, you need to start them manually with `actor.start()` and you might need to do some other adjustments. | 
| 84 |  | - | 
| 85 |  | -- The `BatteryPool.power_distribution_results` method has been enhanced to provide power distribution results in the form of `Power` objects, replacing the previous use of `float` values. | 
| 86 |  | - | 
| 87 |  | -- In the `Request` class: | 
| 88 |  | -  * The attribute `request_timeout_sec` has been updated and is now named `request_timeout` and it is represented by a `timedelta` object rather than a `float`. | 
| 89 |  | -  * The attribute `power` is now presented as a `Power` object, as opposed to a `float`. | 
| 90 |  | - | 
| 91 |  | -- Within the `EVChargerPool.set_bounds` method, the parameter `max_amps` has been redefined as `max_current`, and it is now represented using a `Current` object instead of a `float`. | 
| 92 |  | - | 
| 93 |  | -- The argument `nones_are_zeros` in `FormulaEngine` and related classes and methods is now a keyword-only argument. | 
|  | 9 | +<!-- Here goes notes on how to upgrade from previous versions, including deprecations and what they should be replaced with --> | 
| 94 | 10 | 
 | 
| 95 | 11 | ## New Features | 
| 96 | 12 | 
 | 
| 97 |  | -- Added `DFS` to the component graph | 
| 98 |  | - | 
| 99 |  | -- `BackgroundService`: This new abstract base class can be used to write other classes that runs one or more tasks in the background. It provides a consistent API to start and stop these services and also takes care of the handling of the background tasks. It can also work as an `async` context manager, giving the service a deterministic lifetime and guaranteed cleanup. | 
| 100 |  | - | 
| 101 |  | -  All classes spawning tasks that are expected to run for an indeterminate amount of time are likely good candidates to use this as a base class. | 
| 102 |  | - | 
| 103 |  | -- `Actor`: This new class inherits from `BackgroundService` and it replaces the `@actor` decorator. | 
| 104 |  | - | 
| 105 |  | -- Newly added `min` and `max` functions for Formulas. They can be used as follows: | 
| 106 |  | - | 
| 107 |  | -    ```python | 
| 108 |  | -    formula1.min(formula2) | 
| 109 |  | -    ``` | 
|  | 13 | +<!-- Here goes the main new features and examples or instructions on how to use them --> | 
| 110 | 14 | 
 | 
| 111 | 15 | ## Bug Fixes | 
| 112 | 16 | 
 | 
| 113 |  | -- Fixes a bug in the ring buffer updating the end timestamp of gaps when they are outdated. | 
| 114 |  | - | 
| 115 |  | -- Properly handles PV configurations with no or only some meters before the PV component. | 
| 116 |  | - | 
| 117 |  | -  So far we only had configurations like this: `Meter -> Inverter -> PV`. However the scenario with `Inverter -> PV` is also possible and now handled correctly. | 
| 118 |  | - | 
| 119 |  | -- Fix `consumer_power()` not working certain configurations. | 
| 120 |  | - | 
| 121 |  | -  In microgrids without consumers and no main meter, the formula would never return any values. | 
| 122 |  | - | 
| 123 |  | -- Fix `pv_power` not working in setups with 2 grid meters by using a new reliable function to search for components in the components graph | 
| 124 |  | - | 
| 125 |  | -- Fix `consumer_power` and `producer_power` similar to `pv_power` | 
| 126 |  | - | 
| 127 |  | -- Zero value requests received by the `PowerDistributingActor` will now always be accepted, even when there are non-zero exclusion bounds. | 
| 128 |  | - | 
| 129 |  | -- Hold on to a reference to all streaming tasks in the microgrid API client, so they don't get garbage collected. | 
|  | 17 | +<!-- Here goes notable bug fixes that are worth a special mention or explanation --> | 
0 commit comments