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
28 changes: 28 additions & 0 deletions docs/IO-Geräte & -Aktionen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
### IO-Geräte
IO/GPIO sind analoge und digitale Ein- und Ausgänge, die man meist als Pin- oder Buchsenleiste auf der Platine findet. openWB software2 kann analoge und digitale Eingänge auslesen und analoge sowie digitale Ausgänge schalten. Die Ein- und Ausgänge befinden sich auf dem konfigurierten IO-Gerät, wie zB dem Dimm- & Control-Kit. Um festzulegen, was mit den Informationen aus den Eingängen gemacht werden soll oder welche Ausgänge geschaltet werden sollen, konfigurierst Du IO-Aktionen. Bei der IO-Aktion gibst Du an, welcher Ein- oder Ausgang dafür verwendet werden soll und ggf weiter Aktions-spezifische Einstellungen.

#### Dimm-& Control-Kit
Das Dimm-& Control-Kit besitzt acht analoge Eingänge (AI1-AI8), acht digitale Eingänge (DI1-DI8) und achte digitale Ausgänge (DO1-DO8). Bei den Ausgängen handelt es sich um potentialfreie Relais-Ausgänge mit 5A@28VDC/250VAC.

#### openWB series2-Modell mit AddOn-Platine
Die AddOn-Platine stellt 7 Eingänge und 3 Ausgänge zur Verfügung. WICHTIG: In openWB software 1.9 waren den IOs feste Aktionen zugeordnet, die auch der Platine beschriftet sind. Diese Zuordnung ist in software2 NICHT vorgegeben. Zur einfachen Zuordnung der Pins hier eine Übersicht:
| Pin | Beschriftung |
|---------|---------|
| Eingang 21 | RSE 2 |
| Eingang 24 | RSE 1 |
| Eingang 31 | Taster 3 PV |
| Eingang 32 | Taster 1 Sofortladen |
| Eingang 33 | Taster 4 Stop |
| Eingang 36 | Taster 2 Min+PV |
| Eingang 40 | Taster 5 Standby |
| Ausgang 7 | LED 3 |
| Ausgang 16 | LED 2 |
| Ausgang 18 | LED 1 |


### IO-Aktionen

#### Steuerbare Verbrauchseinrichtungen: Dimmen, Dimmung per Direkt-Steuerung, RSE
Ausführliche Informationen findest Du im gesonderten Wiki-Beitrag [Steuerbare Verbrauchseinrichtungen](https://github.com/openWB/core/wiki/Steuerbare-Verbrauchseinrichtungen)

Weitere IO-Aktionen sind in Arbeit.
12 changes: 12 additions & 0 deletions docs/Steuerbare Verbrauchseinrichtungen nach § 14a.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Der Gesetzgeber sieht verschiedene Möglichkeiten für steuerbare Verbrauchseinrichtungen vor. Für jede steuerbare Verbrauchseinrichtung kann eine andere Option angemeldet werden. Bei der Konfiguration muss deshalb auch immer der/die Ladepunkte angegeben werden, für die die IO-Aktion angewendet werden soll.

### Dimmen
Beim Dimmen wird eine Mindestleistung für alle steuerbaren Verbrauchseinrichtungen nach einer vorgegebene Formel ermittelt. Das Ergebnis dieser Formel muss bei der IO-Aktion `Dimmen` in der Einstellung `Mindestleistung` eingetragen werden. ACHTUNG: Die openWB kann aktuell nur die Ladepunkte berücksichtigen. Sind noch weitere steuerbare Verbraucher angemeldet, muss dies bei der Ermittlung der Mindestleistung berücksichtigt werden. Für die korrekte Ermitllung der Mindestleistung ist der Betreiber, nicht openWB oder die software2 verantwortlich.
Vorhandener Überschuss kann zusätzlich zur Mindestleistung verwendet werden.

### Dimmung per Direkt-Steuerung
Bei der Dimmung per Direkt-Steuerung wird jede steuerbare Verbrauchseinrichtung separat angesteuert und ihr Leistungsbezug auf 4,2kW gedimmt.
Pro steuerbarer Verbrauchseinrichtung muss eine IO-Aktion konfiguriert werden und dort der Ladepunkt und der zugehörige Eingang angegeben werden.

### Rundsteuer-Empfänger-Kontakt (RSE)
Für den RSE-Kontakt kann ein Muster aus verschiedenen Eingängen und ein Prozentwert, auf den die Anschlussleistung begrenzt wird, angegeben werden.
4 changes: 3 additions & 1 deletion docs/_Sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@
* Oberfläche
* [Anzeige - Steuerung](https://github.com/openWB/core/wiki/Anzeige-Steuerung)
* Features
* [Identifikation](https://github.com/openWB/core/wiki/Identifikation)
* [IO-Geräte & -Aktionen](https://github.com/openWB/core/wiki/IO-Geräte-&--Aktionen)
* [OCPP](https://github.com/openWB/core/wiki/OCPP)
* [Strompreisbasiertes Laden](https://github.com/openWB/core/wiki/Strompreisbasiertes-Laden)
* [Identifikation](https://github.com/openWB/core/wiki/Identifikation)
* [Steuerbare Verbrauchseinrichtungen](https://github.com/openWB/core/wiki/Steuerbare-Verbrauchseinrichtungen-nach-§14a)
* Szenarien
* [Typische Anwendungsfälle](https://github.com/openWB/core/wiki/Typische-Anwendungsfälle)
* [Hybrid-System aus Wechselrichter und Speicher](https://github.com/openWB/core/wiki/Hybrid-System-aus-Wechselrichter-und-Speicher)
Expand Down
11 changes: 4 additions & 7 deletions packages/control/algorithm/additional_current.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@
from control.algorithm import common
from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT
from control.loadmanagement import LimitingValue, Loadmanagement
from control.counter import Counter
from control.chargepoint.chargepoint import Chargepoint
from control.algorithm.filter_chargepoints import (get_chargepoints_by_mode_and_counter,
get_preferenced_chargepoint_charging)
from modules.common.utils.component_parser import get_component_name_by_id

log = logging.getLogger(__name__)

Expand All @@ -28,14 +26,14 @@ def set_additional_current(self) -> None:
while len(preferenced_chargepoints):
cp = preferenced_chargepoints[0]
missing_currents, counts = common.get_missing_currents_left(preferenced_chargepoints)
available_currents, limit = Loadmanagement().get_available_currents(missing_currents, counter)
available_currents, limit = Loadmanagement().get_available_currents(missing_currents, counter, cp)
log.debug(f"cp {cp.num} available currents {available_currents} missing currents "
f"{missing_currents} limit {limit}")
cp.data.control_parameter.limit = limit
available_for_cp = common.available_current_for_cp(cp, counts, available_currents, missing_currents)
current = common.get_current_to_set(
cp.data.set.current, available_for_cp, cp.data.set.target_current)
self._set_loadmangement_message(current, limit, cp, counter)
self._set_loadmangement_message(current, limit, cp)
common.set_current_counterdiff(
cp.data.control_parameter.min_current,
current,
Expand All @@ -49,8 +47,7 @@ def set_additional_current(self) -> None:
def _set_loadmangement_message(self,
current: float,
limit: LimitingValue,
chargepoint: Chargepoint,
counter: Counter) -> None:
chargepoint: Chargepoint) -> None:
# Strom muss an diesem Zähler geändert werden
log.debug(
f"current {current} target {chargepoint.data.set.target_current} set current {chargepoint.data.set.current}"
Expand All @@ -60,4 +57,4 @@ def _set_loadmangement_message(self,
round(current, 2) != round(max(
chargepoint.data.control_parameter.required_currents), 2)):
chargepoint.set_state_and_log(f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden"
f"{limit.value.format(get_component_name_by_id(counter.num))}")
f"{limit}")
11 changes: 4 additions & 7 deletions packages/control/algorithm/additional_current_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from unittest.mock import Mock
import pytest

from control.algorithm import additional_current
Expand All @@ -13,15 +12,15 @@
"set_current, limit, expected_msg",
[pytest.param(7, None, None, id="unverändert"),
pytest.param(
6, LimitingValue.CURRENT,
6, LimitingValue.CURRENT.value.format('Garage'),
f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden{LimitingValue.CURRENT.value.format('Garage')}",
id="begrenzt durch Strom"),
pytest.param(
6, LimitingValue.POWER,
6, LimitingValue.POWER.value.format('Garage'),
f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden{LimitingValue.POWER.value.format('Garage')}",
id="begrenzt durch Leistung"),
pytest.param(
6, LimitingValue.UNBALANCED_LOAD,
6, LimitingValue.UNBALANCED_LOAD.value.format('Garage'),
f"Es kann nicht mit der vorgegebenen Stromstärke geladen werden"
f"{LimitingValue.UNBALANCED_LOAD.value.format('Garage')}",
id="begrenzt durch Schieflast"),
Expand All @@ -33,11 +32,9 @@ def test_set_loadmangement_message(set_current, limit, expected_msg, monkeypatch
cp1 = Chargepoint(1, None)
cp1.data = ChargepointData(set=Set(current=set_current),
control_parameter=ControlParameter(required_currents=[8]*3))
mockget_component_name_by_id = Mock(return_value="Garage")
monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id)

# execution
additional_current.AdditionalCurrent()._set_loadmangement_message(7, limit, cp1, Mock())
additional_current.AdditionalCurrent()._set_loadmangement_message(7, limit, cp1)

# evaluation
assert cp1.data.get.state_str == expected_msg
3 changes: 3 additions & 0 deletions packages/control/algorithm/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def mode_and_counter_generator(chargemodes: List) -> Iterable[Tuple[Tuple[Option
counter = data.data.counter_data[f"counter{element['id']}"]
yield mode_tuple, counter


# tested


Expand Down Expand Up @@ -75,6 +76,7 @@ def set_current_counterdiff(diff_curent: float,
data.data.counter_data[counter].update_surplus_values_left(diffs)
else:
data.data.counter_data[counter].update_values_left(diffs)
data.data.io_actions.dimming_set_import_power_left(chargepoint.num, sum(diffs)*230)

chargepoint.data.set.current = current
log.info(f"LP{chargepoint.num}: Stromstärke {current}A")
Expand Down Expand Up @@ -142,6 +144,7 @@ def update_raw_data(preferenced_chargepoints: List[Chargepoint],
data.data.counter_data[counter].update_surplus_values_left(diffs)
else:
data.data.counter_data[counter].update_values_left(diffs)
data.data.io_actions.dimming_set_import_power_left(chargepoint.num, sum(diffs)*230)


def consider_less_charging_chargepoint_in_loadmanagement(cp: Chargepoint, set_current: float) -> bool:
Expand Down
2 changes: 2 additions & 0 deletions packages/control/algorithm/common_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@
from control.ev import Ev
from control.counter import Counter
from control.counter_all import CounterAll
from control.io_device import IoActions


@pytest.fixture(autouse=True)
def cp() -> None:
data.data_init(Mock())
data.data.cp_data = {"cp0": Chargepoint(0, None)}
data.data.io_actions = IoActions()


@pytest.mark.parametrize("set_current, expected_current",
Expand Down
2 changes: 2 additions & 0 deletions packages/control/algorithm/integration_test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from control.counter_all import CounterAll
from control.counter import Counter
from control.ev import Ev
from control.io_device import IoActions
from control.pv import Pv
from control.chargepoint.chargepoint_state import ChargepointState
from test_utils.default_hierarchies import NESTED_HIERARCHY
Expand Down Expand Up @@ -48,6 +49,7 @@ def data_() -> None:
data.data.counter_all_data = CounterAll()
data.data.counter_all_data.data.get.hierarchy = NESTED_HIERARCHY
data.data.counter_all_data.data.config.consider_less_charging = True
data.data.io_actions = IoActions()


@dataclass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
from unittest.mock import Mock
import pytest

from control.algorithm import additional_current
from control.algorithm.integration_test.conftest import ParamsExpectedSetCurrent, assert_expected_current
from control.chargemode import Chargemode
from control import data
from control import data, loadmanagement
from control.algorithm.algorithm import Algorithm
from control.limiting_value import LimitingValue
from dataclass_utils.factories import currents_list_factory
Expand Down Expand Up @@ -63,8 +62,6 @@ def test_start_instant_charging(all_cp_instant_charging_1p, all_cp_not_charging,
data.data.counter_data["counter0"].data.set.raw_power_left = 21310
data.data.counter_data["counter0"].data.set.raw_currents_left = [32, 30, 31]
data.data.counter_data["counter6"].data.set.raw_currents_left = [16, 12, 14]
mockget_component_name_by_id = Mock(return_value="Garage")
monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id)

# execution
Algorithm().calc_current()
Expand Down Expand Up @@ -123,7 +120,7 @@ def test_instant_charging_limit(params: ParamsLimit, all_cp_instant_charging_1p,
data.data.counter_data["counter0"].data.set.raw_currents_left = params.raw_currents_left_counter0
data.data.counter_data["counter6"].data.set.raw_currents_left = params.raw_currents_left_counter6
mockget_component_name_by_id = Mock(return_value="Garage")
monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id)
monkeypatch.setattr(loadmanagement, "get_component_name_by_id", mockget_component_name_by_id)
# execution
Algorithm().calc_current()

Expand Down Expand Up @@ -200,7 +197,7 @@ def test_control_parameter_instant_charging(params: ParamsControlParameter, all_
data.data.counter_data["counter0"].data.set.raw_currents_left = [32]*3
data.data.counter_data["counter6"].data.set.raw_currents_left = [16]*3
mockget_component_name_by_id = Mock(return_value="Garage")
monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id)
monkeypatch.setattr(loadmanagement, "get_component_name_by_id", mockget_component_name_by_id)

# execution
Algorithm().calc_current()
Expand Down
13 changes: 2 additions & 11 deletions packages/control/algorithm/integration_test/pv_charging_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
from unittest.mock import Mock
import pytest

from control.algorithm import additional_current, surplus_controlled
from control.algorithm.integration_test.conftest import ParamsExpectedSetCurrent, assert_expected_current
from control.chargemode import Chargemode
from control import data
from control import data, loadmanagement
from control.algorithm.algorithm import Algorithm
from control.algorithm.algorithm import data as algorithm_data
from control.chargepoint.chargepoint_template import CpTemplate
Expand Down Expand Up @@ -121,8 +120,6 @@ def test_start_pv_delay(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatch)
data.data.counter_data["counter0"].data.set.raw_currents_left = [32, 30, 31]
data.data.counter_data["counter6"].data.set.raw_currents_left = [16, 12, 14]
data.data.counter_data["counter0"].data.set.reserved_surplus = 0
mockget_component_name_by_id = Mock(return_value="Garage")
monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id)

# execution
Algorithm().calc_current()
Expand Down Expand Up @@ -159,8 +156,6 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc
"cp4"].data.control_parameter.state = ChargepointState.SWITCH_ON_DELAY
data.data.cp_data[
"cp5"].data.control_parameter.timestamp_switch_on_off = None
mockget_component_name_by_id = Mock(return_value="Garage")
monkeypatch.setattr(additional_current, "get_component_name_by_id", mockget_component_name_by_id)

# execution
Algorithm().calc_current()
Expand Down Expand Up @@ -228,7 +223,7 @@ def test_surplus(params: ParamsSurplus, all_cp_pv_charging_3p, all_cp_charging_3
data.data.counter_data["counter0"].data.set.raw_currents_left = params.raw_currents_left_counter0
data.data.counter_data["counter6"].data.set.raw_currents_left = params.raw_currents_left_counter6
mockget_component_name_by_id = Mock(return_value="Garage")
monkeypatch.setattr(surplus_controlled, "get_component_name_by_id", mockget_component_name_by_id)
monkeypatch.setattr(loadmanagement, "get_component_name_by_id", mockget_component_name_by_id)

# execution
Algorithm().calc_current()
Expand Down Expand Up @@ -275,8 +270,6 @@ def test_phase_switch(all_cp_pv_charging_3p, all_cp_charging_3p, monkeypatch):
data.data.counter_data["counter0"].data.set.raw_power_left = cases_phase_switch[0].raw_power_left
data.data.counter_data["counter0"].data.set.raw_currents_left = cases_phase_switch[0].raw_currents_left_counter0
data.data.counter_data["counter6"].data.set.raw_currents_left = cases_phase_switch[0].raw_currents_left_counter6
mockget_component_name_by_id = Mock(return_value="Garage")
monkeypatch.setattr(surplus_controlled, "get_component_name_by_id", mockget_component_name_by_id)
mockget_get_phases_chargemode = Mock(return_value=0)
monkeypatch.setattr(algorithm_data.data.general_data, "get_phases_chargemode", mockget_get_phases_chargemode)
data.data.cp_data[
Expand Down Expand Up @@ -305,8 +298,6 @@ def test_phase_switch_1p_3p(all_cp_pv_charging_1p, monkeypatch):
data.data.counter_data["counter0"].data.set.raw_power_left = cases_phase_switch[1].raw_power_left
data.data.counter_data["counter0"].data.set.raw_currents_left = cases_phase_switch[1].raw_currents_left_counter0
data.data.counter_data["counter6"].data.set.raw_currents_left = cases_phase_switch[1].raw_currents_left_counter6
mockget_component_name_by_id = Mock(return_value="Garage")
monkeypatch.setattr(surplus_controlled, "get_component_name_by_id", mockget_component_name_by_id)
mockget_get_phases_chargemode = Mock(return_value=0)
monkeypatch.setattr(algorithm_data.data.general_data, "get_phases_chargemode", mockget_get_phases_chargemode)
data.data.cp_data["cp3"].data.get.currents = [32, 0, 0]
Expand Down
7 changes: 3 additions & 4 deletions packages/control/algorithm/min_current.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_MIN_CURRENT
from control.loadmanagement import Loadmanagement
from control.algorithm.filter_chargepoints import get_chargepoints_by_mode_and_counter
from modules.common.utils.component_parser import get_component_name_by_id

log = logging.getLogger(__name__)

Expand All @@ -24,7 +23,8 @@ def set_min_current(self) -> None:
cp = preferenced_chargepoints[0]
missing_currents, counts = common.get_min_current(cp)
if max(missing_currents) > 0:
available_currents, limit = Loadmanagement().get_available_currents(missing_currents, counter)
available_currents, limit = Loadmanagement().get_available_currents(
missing_currents, counter, cp)
cp.data.control_parameter.limit = limit
available_for_cp = common.available_current_for_cp(
cp, counts, available_currents, missing_currents)
Expand All @@ -34,8 +34,7 @@ def set_min_current(self) -> None:
common.set_current_counterdiff(-(cp.data.set.current or 0), 0, cp)
if limit:
cp.set_state_and_log(
"Ladung kann nicht gestartet werden"
f"{limit.value.format(get_component_name_by_id(counter.num))}")
f"Ladung kann nicht gestartet werden{limit}")
else:
common.set_current_counterdiff(
cp.data.set.target_current,
Expand Down
Loading