Open Marenz opened 5 months ago
Some other ideas, after a long discussion.
If we make the running_status_change
channel send events mapped 1-1 with a singular dispatch object, we can add some utility functions to merge dispatches to cope with different actor's needs.
For example, for the power setting actor, since it works with component pools, it would make sense to merge dispatches with the same selector, which will map with one component pool.
In this case:
dispatch_id=1, type=set_power, selector=component_id:[1], start=1pm, duration=2h, power_w=1000
dispatch_id=2, type=set_power, selector=component_id:[1], start=2pm duration=2h, power_w=1000
dispatch_id=3, type=set_power, selector=component_id:[1,2], start=2pm duration=1h, power_w=2000
dispatch_id=4, type=set_power, selector=component_id:[1], start=3pm duration=2h, power_w=2000
We would normally receive these events:
But it would make more sense to receive this (dispatch_id
info is lost):
The code could look like this:
async for dispatch_event in merge_same_selector(dispatcher.running_status_change.new_receiver()):
match dispatch_event:
Start(dispatch):
actor = PowerSettingActor(dispatch)
actor.start()
self._actors[dispatch.selector] = actor
Stop(dispatch):
if actor := self._actors.get(dispatch.selector):
await actor.stop()
Update(dispatch):
if actor := self._actors.get(dispatch.selector):
await actor.reconfigure(dispatch)
Where merge_same_selector()
does the merging.
Another example, for FCR, we want to override any overlapping dispatches:
dispatch_id=1, type=set_power, selector=component_id:[1], start=1pm, duration=2h, max_power_w=1000
dispatch_id=2, type=set_power, selector=component_id:[1,2], start=2pm duration=2h, max_power_w=1000
dispatch_id=2, type=set_power, selector=component_id:[1], start=3pm duration=2h, max_power_w=2000
We would normally receive these events:
But it would make more sense to receive this (dispatch_id
info is lost):
The code will basically look the same but applying a different filter:
async for dispatch_event in merg_overlapping(dispatcher.running_status_change.new_receiver()):
match dispatch_event:
...
To be able to do this, maybe we need to update the Dispatch.dispatch_id
to be something like int | Merged
(where Merged
is a sentinel class).
Instead of having int | Merged
, the merger functions could return something like
MergedEvent:
event: DispatchEvent # start/stop/config
dispatches: list[Dispatch] # list of involved dispatches
after all, the event
part is what one usually cares about?
I think you care about the dispatch contents, at least the payload. If you get a MergedEvent(Config, [dispatch1, dispatch2, dispatch3])
how do you know which dispatch to use to reconfigure the actor?
I suppose they could be sorted by latest update_time and there could be a
MergedEvent:
event: DispatchEvent # start/stop/config
dispatches: list[Dispatch] # list of involved dispatches, ordered by modification time
@property
def dispatch() -> Dispatch:
"""Return the most recently modified dispatch"""
return self.dispatches[0]
Why latest update time? If you have one dispatch id=2 from 10 to 11, and dispatch id=3 from 11 to 12, and it is 10:30 and someone updates dispatch id=3, then the returned dispatch should be id=2 even when the last updated dispatch is id=3, right? I think you would need to search which dispatch is currently active based on the current time, no?
i'd say there would be no event at all in your example because it wouldn't change the current running state? Only at 11 it would trigger an event containing only id 3?
Yeah, if the payload changed, a "reconfigure"/"update" event should be sent so the actor can act on it.
Only at 11 it would trigger an event containing only id 3?
Yeah, that's what I was thinking of. To put more context to the example, lets say disaptch id=2 says charge 5kw and id=3 says charge 1kw. The actor should switch from 5kw to 1 kw at 11, right?
What's needed?
See https://github.com/frequenz-floss/frequenz-dispatch-python/pull/22#discussion_r1584494188 for details
Proposed solution
No response
Use cases
No response
Alternatives and workarounds
No response
Additional context
No response