Skip to content

Commit 4cc33d0

Browse files
committed
Enhance & extend documentation and examples
Signed-off-by: Mathias L. Baumann <[email protected]>
1 parent 97a4b8e commit 4cc33d0

File tree

3 files changed

+119
-81
lines changed

3 files changed

+119
-81
lines changed

README.md

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,38 +7,61 @@
77
## Introduction
88

99
A highlevel interface for the dispatch API.
10-
The interface is made of the dispatch actor which should run once per microgrid.
11-
It provides two channels for clients:
12-
- "new_dispatches" for newly created dispatches
13-
- "ready_dispatches" for dispatches that are ready to be executed
1410

15-
## Example Usage
11+
See [the documentation](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch) for more information.
12+
13+
## Usage
14+
15+
The [`Dispatcher` class](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher), the main entry point for the API, provides two channels:
16+
17+
* [Lifecycle events](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.lifecycle_events): A channel that sends a message whenever a [Dispatch][frequenz.dispatch.Dispatch] is created, updated or deleted.
18+
* [Running status change](https://frequenz-floss.github.io/frequenz-dispatch-python/v0.1/reference/frequenz/dispatch/#frequenz.dispatch.Dispatcher.running_status_change): Sends a dispatch message whenever a dispatch is ready to be executed according to the schedule or the running status of the dispatch changed in a way that could potentially require the actor to start, stop or reconfigure itself.
19+
20+
### Example using the running status change channel
1621

1722
```python
18-
async def run():
19-
# dispatch helper sends out dispatches when they are due
20-
dispatch_arrived = dispatch_helper.updated_dispatches().new_receiver()
21-
dispatch_ready = dispatch_helper.ready_dispatches().new_receiver()
22-
23-
async for selected in select(dispatch_ready, dispatch_arrived):
24-
if selected_from(selected, dispatch_ready):
25-
dispatch = selected.value
26-
match dispatch.type:
27-
case DISPATCH_TYPE_BATTERY_CHARGE:
28-
battery_pool = microgrid.battery_pool(dispatch.battery_set, task_id)
29-
battery_pool.set_charge(dispatch.power)
30-
...
31-
if selected_from(selected, dispatch_arrived):
32-
match selected.value:
33-
case Created(dispatch):
34-
log.info("New dispatch arrived %s", dispatch)
35-
...
36-
case Updated(dispatch):
37-
log.info("Dispatch updated %s", dispatch)
38-
...
39-
case Deleted(dispatch):
40-
log.info("Dispatch deleted %s", dispatch)
41-
...
23+
import os
24+
import grpc.aio
25+
from unittest.mock import MagicMock
26+
27+
async def run():
28+
host = os.getenv("DISPATCH_API_HOST", "localhost")
29+
port = os.getenv("DISPATCH_API_PORT", "50051")
30+
31+
service_address = f"{host}:{port}"
32+
grpc_channel = grpc.aio.insecure_channel(service_address)
33+
microgrid_id = 1
34+
dispatcher = Dispatcher(microgrid_id, grpc_channel, service_address)
35+
await dispatcher.start()
36+
37+
actor = MagicMock() # replace with your actor
38+
39+
changed_running_status_rx = dispatcher.running_status_change.new_receiver()
40+
41+
async for dispatch in changed_running_status_rx:
42+
match dispatch.running("DEMO_TYPE"):
43+
case RunningState.RUNNING:
44+
print(f"Executing dispatch {dispatch.id}, due on {dispatch.start_time}")
45+
if actor.is_running:
46+
actor.reconfigure(
47+
components=dispatch.selector,
48+
run_parameters=dispatch.payload, # custom actor parameters
49+
dry_run=dispatch.dry_run,
50+
until=dispatch.until,
51+
) # this will reconfigure the actor
52+
else:
53+
# this will start a new actor with the given components
54+
# and run it for the duration of the dispatch
55+
actor.start(
56+
components=dispatch.selector,
57+
run_parameters=dispatch.payload, # custom actor parameters
58+
dry_run=dispatch.dry_run,
59+
until=dispatch.until,
60+
)
61+
case RunningState.STOPPED:
62+
actor.stop() # this will stop the actor
63+
case RunningState.DIFFERENT_TYPE:
64+
pass # dispatch not for this type
4265
```
4366

4467
## Supported Platforms

src/frequenz/dispatch/__init__.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
# License: MIT
22
# Copyright © 2024 Frequenz Energy-as-a-Service GmbH
33

4-
"""A highlevel interface for the dispatch API."""
4+
"""A highlevel interface for the dispatch API.
5+
6+
A small overview of the most important classes in this module:
7+
8+
* [Dispatcher][frequenz.dispatch.Dispatcher]: The entry point for the API.
9+
* [Dispatch][frequenz.dispatch.Dispatch]: A dispatch type with lots of useful extra functionality.
10+
* [Created][frequenz.dispatch.Created],
11+
[Updated][frequenz.dispatch.Updated],
12+
[Deleted][frequenz.dispatch.Deleted]: Dispatch event types.
13+
14+
"""
515

616
from ._dispatch import Dispatch, RunningState
717
from ._dispatcher import Dispatcher, ReceiverFetcher

src/frequenz/dispatch/_dispatcher.py

Lines changed: 56 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -42,59 +42,62 @@ class Dispatcher:
4242
This class provides a highlevel interface to the dispatch API.
4343
It provides two channels:
4444
45-
One that sends a dispatch event message whenever a dispatch is created, updated or deleted.
45+
Lifecycle events:
46+
A channel that sends a dispatch event message whenever a dispatch
47+
is created, updated or deleted.
4648
47-
The other sends a dispatch message whenever a dispatch is ready to be
48-
executed according to the schedule or the running status of the dispatch
49-
changed in a way that could potentially require the actor to start, stop or
50-
reconfigure itself.
49+
Running status change:
50+
Sends a dispatch message whenever a dispatch is ready
51+
to be executed according to the schedule or the running status of the
52+
dispatch changed in a way that could potentially require the consumer to start,
53+
stop or reconfigure itself.
5154
5255
Example: Processing running state change dispatches
56+
```python
57+
import os
58+
import grpc.aio
59+
from frequenz.dispatch import Dispatcher, RunningState
60+
from unittest.mock import MagicMock
61+
62+
async def run():
63+
host = os.getenv("DISPATCH_API_HOST", "localhost")
64+
port = os.getenv("DISPATCH_API_PORT", "50051")
5365
54-
```python
55-
import os
56-
import grpc.aio
57-
from unittest.mock import MagicMock
58-
59-
async def run():
60-
host = os.getenv("DISPATCH_API_HOST", "localhost")
61-
port = os.getenv("DISPATCH_API_PORT", "50051")
62-
63-
service_address = f"{host}:{port}"
64-
grpc_channel = grpc.aio.insecure_channel(service_address)
65-
microgrid_id = 1
66-
dispatcher = Dispatcher(microgrid_id, grpc_channel, service_address)
67-
await dispatcher.start()
68-
69-
actor = MagicMock() # replace with your actor
70-
71-
changed_running_status_rx = dispatcher.running_status_change.new_receiver()
72-
73-
async for dispatch in changed_running_status_rx:
74-
match dispatch.running("DEMO_TYPE"):
75-
case RunningState.RUNNING:
76-
print(f"Executing dispatch {dispatch.id}, due on {dispatch.start_time}")
77-
if actor.is_running:
78-
actor.reconfigure(
79-
components=dispatch.selector,
80-
run_parameters=dispatch.payload, # custom actor parameters
81-
dry_run=dispatch.dry_run,
82-
until=dispatch.until,
83-
) # this will reconfigure the actor
84-
else:
85-
# this will start a new actor with the given components
86-
# and run it for the duration of the dispatch
87-
actor.start(
88-
components=dispatch.selector,
89-
run_parameters=dispatch.payload, # custom actor parameters
90-
dry_run=dispatch.dry_run,
91-
until=dispatch.until,
92-
)
93-
case RunningState.STOPPED:
94-
actor.stop() # this will stop the actor
95-
case RunningState.DIFFERENT_TYPE:
96-
pass # dispatch not for this type
97-
```
66+
service_address = f"{host}:{port}"
67+
grpc_channel = grpc.aio.insecure_channel(service_address)
68+
microgrid_id = 1
69+
dispatcher = Dispatcher(microgrid_id, grpc_channel, service_address)
70+
await dispatcher.start()
71+
72+
actor = MagicMock() # replace with your actor
73+
74+
changed_running_status = dispatcher.running_status_change.new_receiver()
75+
76+
async for dispatch in changed_running_status:
77+
match dispatch.running("DEMO_TYPE"):
78+
case RunningState.RUNNING:
79+
print(f"Executing dispatch {dispatch.id}, due on {dispatch.start_time}")
80+
if actor.is_running:
81+
actor.reconfigure(
82+
components=dispatch.selector,
83+
run_parameters=dispatch.payload, # custom actor parameters
84+
dry_run=dispatch.dry_run,
85+
until=dispatch.until,
86+
) # this will reconfigure the actor
87+
else:
88+
# this will start a new actor with the given components
89+
# and run it for the duration of the dispatch
90+
actor.start(
91+
components=dispatch.selector,
92+
run_parameters=dispatch.payload, # custom actor parameters
93+
dry_run=dispatch.dry_run,
94+
until=dispatch.until,
95+
)
96+
case RunningState.STOPPED:
97+
actor.stop() # this will stop the actor
98+
case RunningState.DIFFERENT_TYPE:
99+
pass # dispatch not for this type
100+
```
98101
99102
Example: Getting notification about dispatch lifecycle events
100103
```python
@@ -127,8 +130,10 @@ async def run():
127130
case _ as unhandled:
128131
assert_never(unhandled)
129132
```
130-
Example: Creating a new dispatch and then modifying it. Note that this uses
131-
the lower-level `Client` class to create and update the dispatch.
133+
134+
Example: Creating a new dispatch and then modifying it.
135+
Note that this uses the lower-level `Client` class to create and update the dispatch.
136+
132137
```python
133138
import os
134139
from datetime import datetime, timedelta, timezone

0 commit comments

Comments
 (0)