def _create_and_activate_strategy_area(self, strategy): self.config = MagicMock() self.config.max_panel_power_W = 160 GlobalConfig.end_date = GlobalConfig.start_date + Duration(days=1) self.area = Area(name="test_area", config=self.config, strategy=strategy) parent = Area(name="parent_area", children=[self.area]) parent.activate() strategy.connected = True market = MagicMock() market.time_slot = GlobalConfig.start_date parent.get_future_market_from_id = lambda _: market self.area.get_future_market_from_id = lambda _: market
class TestExternalMixin(unittest.TestCase): def _create_and_activate_strategy_area(self, strategy): self.config = MagicMock() self.config.max_panel_power_W = 160 GlobalConfig.end_date = GlobalConfig.start_date + Duration(days=1) self.area = Area(name="test_area", config=self.config, strategy=strategy) parent = Area(name="parent_area", children=[self.area]) parent.activate() strategy.connected = True market = MagicMock() market.time_slot = GlobalConfig.start_date parent.get_future_market_from_id = lambda _: market self.area.get_future_market_from_id = lambda _: market def test_dispatch_tick_frequency_gets_calculated_correctly(self): self.external_strategy = LoadHoursExternalStrategy(100) self._create_and_activate_strategy_area(self.external_strategy) d3a.models.strategy.external_strategies.DISPATCH_EVENT_TICK_FREQUENCY_PERCENT = 20 self.config.ticks_per_slot = 90 assert self.external_strategy._dispatch_tick_frequency == 18 self.config.ticks_per_slot = 10 assert self.external_strategy._dispatch_tick_frequency == 2 self.config.ticks_per_slot = 100 assert self.external_strategy._dispatch_tick_frequency == 20 self.config.ticks_per_slot = 99 assert self.external_strategy._dispatch_tick_frequency == 19 d3a.models.strategy.external_strategies.DISPATCH_EVENT_TICK_FREQUENCY_PERCENT = 50 self.config.ticks_per_slot = 90 assert self.external_strategy._dispatch_tick_frequency == 45 self.config.ticks_per_slot = 10 assert self.external_strategy._dispatch_tick_frequency == 5 self.config.ticks_per_slot = 100 assert self.external_strategy._dispatch_tick_frequency == 50 self.config.ticks_per_slot = 99 assert self.external_strategy._dispatch_tick_frequency == 49 @parameterized.expand([[LoadHoursExternalStrategy(100)], [PVExternalStrategy(2, max_panel_power_W=160)], [StorageExternalStrategy()]]) def test_dispatch_event_tick_to_external_agent(self, strategy): d3a.models.strategy.external_strategies.DISPATCH_EVENT_TICK_FREQUENCY_PERCENT = 20 self._create_and_activate_strategy_area(strategy) self.config.ticks_per_slot = 90 assert strategy._dispatch_tick_frequency == 18 self.area.current_tick = 1 strategy._dispatch_event_tick_to_external_agent() strategy.redis.publish_json.assert_not_called() self.area.current_tick = 17 strategy._dispatch_event_tick_to_external_agent() strategy.redis.publish_json.assert_not_called() self.area.current_tick = 18 strategy._dispatch_event_tick_to_external_agent() strategy.redis.publish_json.assert_called_once() assert strategy.redis.publish_json.call_args_list[0][0][ 0] == "test_area/events/tick" result = strategy.redis.publish_json.call_args_list[0][0][1] result.pop('area_uuid') assert result == \ {'device_info': strategy._device_info_dict, 'event': 'tick', 'slot_completion': '20%'} strategy.redis.reset_mock() strategy.redis.publish_json.reset_mock() self.area.current_tick = 35 strategy._dispatch_event_tick_to_external_agent() strategy.redis.publish_json.assert_not_called() self.area.current_tick = 36 strategy._dispatch_event_tick_to_external_agent() strategy.redis.publish_json.assert_called_once() assert strategy.redis.publish_json.call_args_list[0][0][ 0] == "test_area/events/tick" result = strategy.redis.publish_json.call_args_list[0][0][1] result.pop('area_uuid') assert result == \ {'device_info': strategy._device_info_dict, 'event': 'tick', 'slot_completion': '40%'} @parameterized.expand([[LoadHoursExternalStrategy(100)], [PVExternalStrategy(2, max_panel_power_W=160)], [StorageExternalStrategy()]]) def test_dispatch_event_trade_to_external_agent(self, strategy): strategy._track_energy_sell_type = lambda _: None self._create_and_activate_strategy_area(strategy) market = self.area.get_future_market_from_id(1) self.area._markets.markets = {1: market} strategy.state.available_energy_kWh = {market.time_slot: 1000.0} strategy.state.pledged_sell_kWh = {market.time_slot: 0.0} strategy.state.offered_sell_kWh = {market.time_slot: 0.0} current_time = now() trade = Trade('id', current_time, Offer('offer_id', now(), 20, 1.0, 'test_area'), 'test_area', 'parent_area', fee_price=0.23) strategy.event_trade(market_id="test_market", trade=trade) assert strategy.redis.publish_json.call_args_list[0][0][ 0] == "test_area/events/trade" call_args = strategy.redis.publish_json.call_args_list[0][0][1] assert call_args['trade_id'] == trade.id assert call_args['event'] == "trade" assert call_args['price'] == 20 assert call_args['energy'] == 1.0 assert call_args['fee_price'] == 0.23 assert call_args['offer_id'] == trade.offer.id assert call_args['residual_id'] == "None" assert call_args['time'] == current_time.isoformat() assert call_args['seller'] == trade.seller assert call_args['buyer'] == "anonymous" assert call_args['device_info'] == strategy._device_info_dict def test_device_info_dict_for_load_strategy_reports_required_energy(self): strategy = LoadHoursExternalStrategy(100) self._create_and_activate_strategy_area(strategy) strategy.energy_requirement_Wh[strategy.market.time_slot] = 0.987 assert strategy._device_info_dict["energy_requirement_kWh"] == 0.000987 def test_device_info_dict_for_pv_strategy_reports_available_energy(self): strategy = PVExternalStrategy(2, max_panel_power_W=160) self._create_and_activate_strategy_area(strategy) strategy.state.available_energy_kWh[strategy.market.time_slot] = 1.123 assert strategy._device_info_dict["available_energy_kWh"] == 1.123 def test_device_info_dict_for_storage_strategy_reports_battery_stats(self): strategy = StorageExternalStrategy(battery_capacity_kWh=0.5) self._create_and_activate_strategy_area(strategy) strategy.state.energy_to_sell_dict[strategy.market.time_slot] = 0.02 strategy.state.energy_to_buy_dict[strategy.market.time_slot] = 0.03 strategy.state._used_storage = 0.01 assert strategy._device_info_dict["energy_to_sell"] == 0.02 assert strategy._device_info_dict["energy_to_buy"] == 0.03 assert strategy._device_info_dict["used_storage"] == 0.01 assert strategy._device_info_dict["free_storage"] == 0.49 @parameterized.expand([[LoadHoursExternalStrategy(100)], [PVExternalStrategy(2, max_panel_power_W=160)], [StorageExternalStrategy()]]) def test_register_device(self, strategy): self.config = MagicMock() self.device = Area(name="test_area", config=self.config, strategy=strategy) payload = {"data": json.dumps({"transaction_id": str(uuid.uuid4())})} self.device.strategy.owner = self.device assert self.device.strategy.connected is False self.device.strategy._register(payload) self.device.strategy.register_on_market_cycle() assert self.device.strategy.connected is True self.device.strategy._unregister(payload) self.device.strategy.register_on_market_cycle() assert self.device.strategy.connected is False payload = {"data": json.dumps({"transaction_id": None})} with self.assertRaises(ValueError): self.device.strategy._register(payload) with self.assertRaises(ValueError): self.device.strategy._unregister(payload)