1616from frequenz .sdk ._internal ._asyncio import cancel_and_await
1717from frequenz .sdk .actor import ResamplerConfig
1818from frequenz .sdk .microgrid import _data_pipeline
19+ from frequenz .sdk .microgrid ._graph import _MicrogridComponentGraph
1920from frequenz .sdk .microgrid .client import Connection
2021from frequenz .sdk .microgrid .component import (
2122 Component ,
@@ -53,16 +54,17 @@ class MockMicrogrid: # pylint: disable=too-many-instance-attributes
5354
5455 def __init__ ( # pylint: disable=too-many-arguments
5556 self ,
56- grid_meter : bool ,
57+ grid_meter : bool | None = None ,
5758 api_client_streaming : bool = False ,
5859 num_values : int = 2000 ,
5960 sample_rate_s : float = 0.01 ,
6061 num_namespaces : int = 1 ,
62+ graph : _MicrogridComponentGraph | None = None ,
6163 ):
6264 """Create a new instance.
6365
6466 Args:
65- grid_meter: whether there is a meter successor of the GRID component.
67+ grid_meter: optional, whether there is a meter successor of the GRID component.
6668 api_client_streaming: whether the mock client should be configured to stream
6769 raw data from the API client.
6870 num_values: number of values to generate for each component.
@@ -71,28 +73,76 @@ def __init__( # pylint: disable=too-many-arguments
7173 to. Useful in tests where multiple namespaces (logical_meter,
7274 battery_pool, etc) are used, and the same metric is used by formulas in
7375 different namespaces.
76+ graph: optional, a graph of components to use instead of the default grid
77+ layout. If specified, grid_meter must be None.
7478 """
75- self ._components : Set [Component ] = set (
76- [
77- Component (1 , ComponentCategory .GRID ),
78- ]
79+ if grid_meter is not None and graph is not None :
80+ raise ValueError ("grid_meter and graph are mutually exclusive" )
81+
82+ self ._components : Set [Component ] = (
83+ set (
84+ [
85+ Component (1 , ComponentCategory .GRID ),
86+ ]
87+ )
88+ if graph is None
89+ else graph .components ()
90+ )
91+
92+ self ._connections : Set [Connection ] = (
93+ set () if graph is None else graph .connections ()
7994 )
80- self . _connections : Set [ Connection ] = set ()
81- self ._id_increment = 0
95+
96+ self ._id_increment = 0 if graph is None else len ( self . _components )
8297 self ._api_client_streaming = api_client_streaming
8398 self ._num_values = num_values
8499 self ._sample_rate_s = sample_rate_s
85100 self ._namespaces = num_namespaces
86101
87102 self ._connect_to = self .grid_id
88103
89- self .chp_ids : list [int ] = []
90- self .battery_inverter_ids : list [int ] = []
91- self .pv_inverter_ids : list [int ] = []
92- self .battery_ids : list [int ] = []
93- self .evc_ids : list [int ] = []
94- self .meter_ids : list [int ] = []
95- self .bat_inv_map : dict [int , int ] = {}
104+ def filter_comp (category : ComponentCategory ) -> list [int ]:
105+ if graph is None :
106+ return []
107+ return list (
108+ map (
109+ lambda c : c .component_id ,
110+ graph .components (component_category = set ([category ])),
111+ )
112+ )
113+
114+ def inverters (comp_type : InverterType ) -> list [int ]:
115+ if graph is None :
116+ return []
117+
118+ return [
119+ c .component_id
120+ for c in graph .components (
121+ component_category = set ([ComponentCategory .INVERTER ])
122+ )
123+ if c .type == comp_type
124+ ]
125+
126+ self .chp_ids : list [int ] = filter_comp (ComponentCategory .CHP )
127+ self .battery_ids : list [int ] = filter_comp (ComponentCategory .BATTERY )
128+ self .evc_ids : list [int ] = filter_comp (ComponentCategory .EV_CHARGER )
129+ self .meter_ids : list [int ] = filter_comp (ComponentCategory .METER )
130+
131+ self .battery_inverter_ids : list [int ] = inverters (InverterType .BATTERY )
132+ self .pv_inverter_ids : list [int ] = inverters (InverterType .SOLAR )
133+
134+ self .bat_inv_map : dict [int , int ] = (
135+ {}
136+ if graph is None
137+ else {
138+ # Hacky, ignores multiple batteries behind one inverter
139+ list (graph .successors (c .component_id ))[0 ].component_id : c .component_id
140+ for c in graph .components (
141+ component_category = set ([ComponentCategory .INVERTER ])
142+ )
143+ if c .type == InverterType .BATTERY
144+ }
145+ )
96146
97147 self .evc_component_states : dict [int , EVChargerComponentState ] = {}
98148 self .evc_cable_states : dict [int , EVChargerCableState ] = {}
0 commit comments