Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from control.counter import Set as CounterSet
from control.counter_all import CounterAll
from control.pv import Pv, PvData
from control.pv import Config as PvConfig
from control.pv import Get as PvGet
from helpermodules import hardware_configuration, pub, timecheck
from modules.chargepoints.mqtt.chargepoint_module import ChargepointModule
Expand Down Expand Up @@ -155,7 +156,7 @@ def data_() -> None:
set=Mock(spec=BatSet, power_limit=None)))})
data.data.pv_data.update({"pv1": Mock(spec=Pv, data=Mock(
spec=PvData, get=Mock(spec=PvGet, power=-10000, daily_exported=6000, exported=27000, currents=None,
fault_state=0)))})
fault_state=0), config=Mock(spec=PvConfig, max_ac_out=10000)))})
data.data.counter_data.update({
"counter0": Mock(spec=Counter, data=Mock(spec=CounterData, get=Mock(
spec=CounterGet, currents=[40]*3, power=6200, daily_imported=45000, daily_exported=3000, fault_state=0))),
Expand Down
52 changes: 16 additions & 36 deletions packages/control/bat_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,9 @@
from control import data
from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT
from control.algorithm.filter_chargepoints import get_chargepoints_by_chargemodes
from control.bat import Bat
from control.pv import Pv
from helpermodules.constants import NO_ERROR
from modules.common.abstract_device import AbstractDevice
from modules.common.fault_state import FaultStateLevel

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -144,48 +143,32 @@ def calc_power_for_all_components(self):
except Exception:
log.exception("Fehler im Bat-Modul")

def _max_bat_power_hybrid_system(self, battery: Bat) -> float:
def _inverter_limited_power(self, inverter: Pv) -> float:
"""gibt die maximale Entladeleistung des Speichers zurück, bis die maximale Ausgangsleistung des WR erreicht
ist."""
# tested
parent = data.data.counter_all_data.get_entry_of_parent(battery.num)
if parent.get("type") == "inverter":
parent_data = data.data.pv_data[f"pv{parent['id']}"].data
# Wenn vom PV-Ertrag der Speicher geladen wird, kann diese Leistung bis zur max Ausgangsleistung des WR
# genutzt werden.
if parent_data.config.max_ac_out > 0:
max_bat_discharge_power = parent_data.config.max_ac_out + \
parent_data.get.power + battery.data.get.power
return max_bat_discharge_power, True
else:
battery.data.get.fault_state = FaultStateLevel.ERROR.value
battery.data.get.fault_str = self.ERROR_CONFIG_MAX_AC_OUT
raise ValueError(self.ERROR_CONFIG_MAX_AC_OUT)
# Wenn vom PV-Ertrag der Speicher geladen wird, kann diese Leistung bis zur max Ausgangsleistung des WR
# genutzt werden.
if inverter.data.config.max_ac_out > 0:
return max(inverter.data.get.power * -1 - inverter.data.config.max_ac_out, 0)
else:
# Kein Hybrid-WR
# Maximal die Speicher-Leistung als Entladeleistung nutzen, um nicht unnötig Bezug zu erzeugen.
return abs(battery.data.get.power) + 50, False
return 0

def _limit_bat_power_discharge(self, required_power):
"""begrenzt die für den Algorithmus benötigte Entladeleistung des Speichers, wenn die maximale Ausgangsleistung
des WR erreicht ist."""
available_power = 0
hybrid = False
inverter_limited_power = 0
if required_power > 0:
# Nur wenn der Speicher entladen werden soll, fließt Leistung durch den WR.
for battery in data.data.bat_data.values():
for inverter in data.data.pv_data.values():
try:
available_power_bat, hybrid_bat = self._max_bat_power_hybrid_system(battery)
if hybrid_bat:
hybrid = True
available_power += available_power_bat
inverter_limited_power += self._inverter_limited_power(inverter)
except Exception:
log.exception(f"Fehler im Bat-Modul {battery.num}")
if hybrid:
if required_power > available_power:
log.debug(f"Verbleibende Speicher-Leistung durch maximale Ausgangsleistung auf {available_power}W"
" begrenzt.")
return min(required_power, available_power)
log.exception(f"Fehler im Bat-Modul {inverter.num}")
if inverter_limited_power > 0:
required_power = max(required_power-inverter_limited_power, 0)
log.debug(f"Verbleibende Speicher-Leistung durch maximale Ausgangsleistung auf {required_power}W"
" begrenzt.")
return required_power

def setup_bat(self):
Expand Down Expand Up @@ -332,10 +315,7 @@ def get_power_limit(self):
if self.data.set.power_limit is None:
power_limit = None
else:
power_limit = min(self._max_bat_power_hybrid_system(
data.data.bat_data[f"bat{bat_component.component_config.id}"])[0], remaining_power_limit)
remaining_power_limit -= power_limit
remaining_power_limit = max(remaining_power_limit, 0)
power_limit = self._limit_bat_power_discharge(remaining_power_limit)

data.data.bat_data[f"bat{bat_component.component_config.id}"].data.set.power_limit = power_limit

Expand Down
86 changes: 30 additions & 56 deletions packages/control/bat_all_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from dataclasses import dataclass, field
from typing import List, Optional
from unittest.mock import Mock
from unittest.mock import MagicMock, Mock
import pytest
from packages.conftest import hierarchy_standard
from control import bat_all
Expand All @@ -27,66 +27,40 @@ def data_fixture() -> None:


@pytest.mark.parametrize(
"parent, bat_power, pv_power, expected_power_hybrid",
"max_ac_out, power, expected_result",
[
pytest.param({"id": 6, "type": "counter", "children": [
{"id": 2, "type": "bat", "children": []}]}, 100, -6400, (150, False),
id="kein Hybrid-System, Speicher wird geladen"),
pytest.param({"id": 6, "type": "counter", "children": [
{"id": 2, "type": "bat", "children": []}]}, -100, -6400, (150, False),
id="kein Hybrid-System, Speicher wird entladen"),
pytest.param({"id": 1, "type": "inverter", "children": []}, 1200, -6400, (2000, True),
id="Speicher lädt mit 1200W, max 2000W zusätzliche Entladeleistung bis WR max"),
pytest.param({"id": 1, "type": "inverter", "children": []}, 600, -6400, (1400, True),
id="Speicher lädt mit 600W, max 1400W zusätzliche Entladeleistung bis WR max"),
pytest.param({"id": 1, "type": "inverter", "children": []}, 0, -6400, (800, True),
id="Speicher neutral, max 800W Entladeleistung bis WR max"),
pytest.param({"id": 1, "type": "inverter", "children": []}, -600, -6400, (200, True),
id="Speicher entlädt mit 600W, max 200W Entladeleistung bis WR max"),
pytest.param({"id": 1, "type": "inverter", "children": []}, -800, -6400, (0, True),
id="Speicher entlädt mit 800W, maximale Entladeleistung des WR erreicht"),
pytest.param({"id": 1, "type": "inverter", "children": []}, 1200, -7200, (1200, True),
id="Speicher lädt mit 1200W, max 1200W zusätzliche Entladeleistung bis WR max"),
pytest.param({"id": 1, "type": "inverter", "children": []}, 600, -7200, (600, True),
id="Speicher lädt mit 600W, max 600W zusätzliche Entladeleistung bis WR max"),
pytest.param({"id": 1, "type": "inverter", "children": []}, 0, -7200, (0, True),
id="Speicher neutral, maximale Entladeleistung des WR erreicht"),
pytest.param({"id": 1, "type": "inverter", "children": []}, 1200, -7800, (600, True),
id="Speicher lädt mit 1200W, max 600W zusätzliche Entladeleistung bis WR max"),
pytest.param({"id": 1, "type": "inverter", "children": []}, 600, -7800, (0, True),
id="Speicher lädt mit 600W, maximale Entladeleistung des WR erreicht"),
])
def test_max_bat_power_hybrid_system(parent, bat_power, pv_power, expected_power_hybrid, data_fixture, monkeypatch):
# setup
# pv1-Data: max_ac_out 7200
mock_get_entry_of_parent = Mock(return_value=parent)
monkeypatch.setattr(data.data.counter_all_data, "get_entry_of_parent", mock_get_entry_of_parent)
data.data.pv_data["pv1"].data.get.power = pv_power

b = BatAll()
bat2 = Bat(2)
bat2.data.get.power = bat_power

# execution
power = b._max_bat_power_hybrid_system(bat2)

# evaluation
assert power == expected_power_hybrid
pytest.param(5000, -6000, 1000, id="Leistung überschreitet max_ac_out"),
pytest.param(5000, -4000, 0, id="Leistung liegt unter max_ac_out"),
pytest.param(5000, 0, 0, id="Keine Leistung (power = 0)"),
pytest.param(0, -6000, 0, id="max_ac_out ist 0"),
],
)
def test_inverter_limited_power(max_ac_out, power, expected_result):
# Mock für die Pv-Klasse
inverter = Pv(1)
inverter.data.config.max_ac_out = max_ac_out
inverter.data.get.power = power
bat_all = BatAll()

# Aufruf der zu testenden Funktion
result = bat_all._inverter_limited_power(inverter)

# Überprüfung des Ergebnisses
assert result == expected_result


@pytest.mark.parametrize(
"required_power, return_max_bat_power_hybrid_system, expected_power",
"required_power, return_inverter_limited_power, expected_power",
[
pytest.param(1000, (1100, True), 1000, id="maximale Entladeleistung nicht erreicht"),
pytest.param(1000, (900, True), 900, id="maximale Entladeleistung erreicht"),
pytest.param(-1000, (10, True), -1000, id="Speicher soll nicht mehr entladen werden"),
pytest.param(1000, (900, False), 1000, id="kein Hybrid-System"),
pytest.param(1000, 0, 1000, id="maximale Entladeleistung nicht erreicht"),
pytest.param(1000, 100, 900, id="maximale Entladeleistung erreicht"),
pytest.param(-1000, 10, -1000, id="Speicher soll nicht mehr entladen werden"),
])
def test_limit_bat_power_discharge(required_power, return_max_bat_power_hybrid_system, expected_power, monkeypatch):
def test_limit_bat_power_discharge(required_power, return_inverter_limited_power, expected_power, monkeypatch):
# setup
data.data.bat_data = {"bat2": Bat(2)}
mock_max_bat_power_hybrid_system = Mock(return_value=return_max_bat_power_hybrid_system)
monkeypatch.setattr(BatAll, "_max_bat_power_hybrid_system", mock_max_bat_power_hybrid_system)
data.data.pv_data = {"pv2": Pv(2)}
mock_inverter_limited_power = Mock(return_value=return_inverter_limited_power)
monkeypatch.setattr(BatAll, "_inverter_limited_power", mock_inverter_limited_power)

b = BatAll()

Expand Down Expand Up @@ -168,8 +142,8 @@ def test_get_charging_power_left(params: Params, caplog, data_fixture, monkeypat
b.data.get.power = params.power
data.data.bat_data["bat0"] = b
data.data.general_data.data.chargemode_config.pv_charging = params.config
mock__max_bat_power_hybrid_system = Mock(return_value=(params.power, None))
monkeypatch.setattr(BatAll, "_max_bat_power_hybrid_system", mock__max_bat_power_hybrid_system)
mock_limit_bat_power_discharge = MagicMock(side_effect=lambda x: x)
monkeypatch.setattr(BatAll, "_limit_bat_power_discharge", mock_limit_bat_power_discharge)

# execution
b_all._get_charging_power_left()
Expand Down
3 changes: 2 additions & 1 deletion packages/helpermodules/measurement_logging/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from threading import Event
import pytest

from control.bat import Bat
from control.chargepoint import chargepoint
from control.chargepoint.chargepoint_all import AllChargepoints
from control import bat_all, counter, pv_all, pv
Expand All @@ -10,7 +11,7 @@
@pytest.fixture(autouse=True)
def data_module() -> None:
data.data_init(Event())
data.data.bat_data.update({"all": bat_all.BatAll(), "bat2": bat_all.Bat(2)})
data.data.bat_data.update({"all": bat_all.BatAll(), "bat2": Bat(2)})
data.data.counter_data.update({"counter0": counter.Counter(0)})
data.data.cp_all_data = AllChargepoints()
data.data.cp_data.update({"cp4": chargepoint.Chargepoint(
Expand Down