Skip to content

Commit 364df0b

Browse files
LKuemmelndrsnhsBrett-S-OWBcr0ivuffiraa72
authored
bidi (#2575)
* bidi draft * typos * fixes * fixes * fixes * flake8 * pytest * fix pytest * rename * rename pro mode * fixes * workaround max current * fix zero point control mode * max_discharge_power * calc max discharge power * Exception * fix extend meter check (#2518) * fix phases scheduled charging combined with time charging (#2521) * add path (#2520) * create empty thread errors log (#2524) * loose limits for hardware check (#2525) * loose limits for hardware check * fix * fixes * set zeor point to evu counter * fix * fix max bidi current * flake8 * Only display time charging plans if time charging is activated (#2523) * Build Web Theme: Koala * use queue and listener for logging (#2528) * use queue and listener for logging * flake8 * log top (#2529) * fix merge update_config * disable screen timeout (#2535) * SolarEdge Speichersteuerung mit Battery Index (#2269) * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update bat.py * Update power_limit sign * fix SDM120 (#2536) * throttled state (#2541) * Fix SoC slider displayed for standard fahrzeug (#2540) * Build Web Theme: Koala * GoodWe second battery; fix no attribute error (#2539) * add second battery and update config * fix no attribute error * fix register adresses * GoodWe second battery - fix index detection (#2546) * add second battery and update config * fix no attribute error * fix register adresses * detect battery index * Wiki SolarEdge Speichersteuerung (#2537) * Wiki SolarEdge Speichersteuerung * Change Default SoC-Reserve * Change Default min_soc (#2543) * Fix - New Vehicle soc slider visible when SoC module present (Vehicle Cards) (#2542) * Build Web Theme: Koala * remove unused assignment of response headers (#2544) * Update requirements.txt - rise version of pycarwings3 to 0.7.14 (#2545) * Create __init__.py * Add files via upload * Add files via upload includes newest Base URL to Nissan API: https://gdcportalgw.its-mo.com/api_v230317_NE/gdc/ * Add files via upload copied 1:1 from OpenWB V1.9 * Add files via upload included module "fetch-soc" is derived from former soc.py in OpenWB V1.9 * Update soc.py * Update soc.py * Update soc.py * Update soc.py * Update responses.py * Update packages/modules/vehicles/leaf/soc.py Co-authored-by: LKuemmel <76958050+LKuemmel@users.noreply.github.com> * Update soc.py Ich habe Anzahl der Wartezyklen noch von 3 auf 9 erhöht, also insgesamt 3 Minuten Wartezeit. Bis dahin müsste der Nissan Server den Leaf in jedem Fall erreicht haben. Falls nicht, kehrt requestSoc() nach drei Minuten ohne Update des SoC auf dem Server zurück und das anschließende readSoc holt sich dann halt nur den alten SoC vom Server. In der Zeit haben die Funktionen von pycarwings2 und responses auch genug Zeit für Einträge ins Logging für eine evtl. notwendige Fehleranalyse. * Update __init__.py empty line at end of file removed accoring to warning from test run on Jul 15. * Update __init__.py * Update __init__.py * Update __init__.py empty line removed at end of file * Update pycarwings2.py At import instruction wildcard * replaced by the names of the classes to be imported * Update pycarwings2.py trailing spaces removed * Update __init__.py Empty line at end of file removed * Update soc.py missing whitespace after "," added * Update __init__.py * Delete packages/modules/vehicles/leaf/__init__.py still a LF inside, that I can't delete. So I will replace __init__.py by a really empty file to get flake 8 quiet * Add files via upload * Update requirements.txt pycarwings2 added to load this library (for the SOC module of Nissan Leaf) during start * Update packages/modules/vehicles/leaf/soc.py Co-authored-by: LKuemmel <76958050+LKuemmel@users.noreply.github.com> * Delete packages/modules/vehicles/leaf/pycarwings2.py File deleted at this position as requested * Delete packages/modules/vehicles/leaf/responses.py File deleted at this position as requested * Delete packages/modules/vehicles/leaf/soc.py soc.py using pycarwings2 removed * Add files via upload api.py using pycarwings3 uploaded. api_wo_CarState.py using pycarwings3 uploaded. test_fetch_soc.py using fetch_soc within api_wo_CarState uploaded. test_fetch_soc.py is able to run standalone, i.e. without OpenWB overhead. It prints the whole logging of interaction with the Nissan server on console and finishes with the SoC of the Nissan Leaf. Enter your user ID and password before running. This package is using https://github.com/ev-freaks/pycarwings3 from @remuslazar * Add files via upload * Add files via upload * Add files via upload * Add files via upload replace requirements.txt with a version that includes pycarwings3. * Update api.py Definition/Import für Klasse CarState ergänzt. * Update api.py Name of parameter "vnum" within fetch_soc() changed to "charge_point". * Update api.py chargepoint * Update config.py variable "region" added. variable "name" changed to "Nissan Leaf/NV200 -05.2019 (experimental)". * Update soc.py variable "region" added parameter "calc_while_charging" added and preset to False * Update api.py variable "region" added * Update config.py Variable "region" added in line 5. * Update api.py * Update api.py Parameter range added * Update api.py Import-Pfad zur config.py korrigiert * Update api.py fetching time stamp added * Update soc.py "charge_point" renamed to "vehicle" (missunderstanding) * Update api.py "chargepoint" renamed to "vehicle" "LP" renamed to "vehicle" * Delete packages/modules/vehicles/leaf/api_wo_CarState.py needed for test purpose only * Delete packages/modules/vehicles/leaf/test_fetch_soc.py needed for test purpose only * Update soc.py trailing whitespaces removed * Update api.py according to flake8: trailing whitespace removed. import of LeafSoc und LeafConfiguration removed (not used). comment shortened. blank line added * Update api.py whitespaces removed * Update soc.py calc_while_charging removed * Update soc.py variable "vehicle" renamed to "charge_point" * Update soc.py trailing whitespaces removed * Mock pycarwings3 for pytest * Update requirements.txt - rise version of pycarwings3 to 0.7.14 The vehicle module for fetching the SoC of NISSAN Leaf is using the library pycarwings3. NISSAN has changed the URL of its API as well as the method of encryption from Blowfish to AES. Details see remuslazar/homeassistant-carwings#100 New API is at URL https://gdcportalgw.its-mo.com/api_v250205_NE/gdc/ Many thanks to @remuslazar and @zwartevogel who already updated pycarwings3 to version 0.7.14, see ev-freaks/pycarwings3#14 and remuslazar/homeassistant-carwings#101 With this PR the changed pycarwings3 library will be introduced into openWB2 V2.1.8 Alpha.1 &.2 by rising the version No of pycarwings3 from 0.7.13 to 0.7.14 in requirements.txt. --------- Co-authored-by: LKuemmel <76958050+LKuemmel@users.noreply.github.com> * fix manual soc: manual_soc=0% (#2548) * fix manual soc: manual_soc=0% * flake8 * get counter state (#2549) * Widen the main layout area and display 3 cards on screen > 1400px (#2547) * pytest * Build Web Theme: Koala * Revert "Widen the main layout area and display 3 cards on screen > 1400px (#2…" (#2550) This reverts commit 2819727. * Build Web Theme: Koala * fix power limit init (#2555) * new SoC module Cupra (#2553) * add cupra connect soc * add docu * fix manual soc (#2560) * reset fault state at init for tariffs, backup clouds and vehicles (#2562) * fix template id must be int not str (#2566) * fix template id must be int not str * flake8 * adjust factors of phase values (#2564) * fix Meter_Power Nonetype (#2563) * evu kit: clean up code (#2559) * evu kit: clean up code * flake8 * component-wise error handling (#2557) * info-box for new features (#2515) * info-box for new features * flake8 * fix component-wise error handling (#2568) * fix merge * add max dis/charge power * test charge_template * integration test bidi charging * draft * bidi in scheduled charging * fixes * fixes * fix test * unused code --------- Co-authored-by: ndrsnhs <156670705+ndrsnhs@users.noreply.github.com> Co-authored-by: BrettS <168732306+Brett-S-OWB@users.noreply.github.com> Co-authored-by: LKuemmel <LKuemmel@users.noreply.github.com> Co-authored-by: cr0i <christoph@rischke.net> Co-authored-by: vuffiraa72 <u.mersewsky+github@gmail.com> Co-authored-by: mekrapp <158028484+mekrapp@users.noreply.github.com>
1 parent 9e91cf9 commit 364df0b

27 files changed

Lines changed: 503 additions & 178 deletions

packages/control/algorithm/algorithm.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from control import data
55
from control.algorithm import common
66
from control.algorithm.additional_current import AdditionalCurrent
7+
from control.algorithm.bidi_charging import Bidi
78
from control.algorithm.min_current import MinCurrent
89
from control.algorithm.no_current import NoCurrent
910
from control.algorithm.surplus_controlled import SurplusControlled
@@ -14,6 +15,7 @@
1415
class Algorithm:
1516
def __init__(self):
1617
self.additional_current = AdditionalCurrent()
18+
self.bidi = Bidi()
1719
self.min_current = MinCurrent()
1820
self.no_current = NoCurrent()
1921
self.surplus_controlled = SurplusControlled()
@@ -32,14 +34,17 @@ def calc_current(self) -> None:
3234
log.info("**Soll-Strom setzen**")
3335
common.reset_current_to_target_current()
3436
self.additional_current.set_additional_current()
37+
self.surplus_controlled.set_required_current_to_max()
38+
log.info("**PV-geführten Strom setzen**")
3539
counter.limit_raw_power_left_to_surplus(self.evu_counter.calc_raw_surplus())
3640
if self.evu_counter.data.set.surplus_power_left > 0:
37-
log.info("**PV-geführten Strom setzen**")
3841
common.reset_current_to_target_current()
39-
self.surplus_controlled.set_required_current_to_max()
4042
self.surplus_controlled.set_surplus_current()
4143
else:
42-
log.info("**Keine Leistung für PV-geführtes Laden übrig.**")
44+
log.info("Keine Leistung für PV-geführtes Laden übrig.")
45+
log.info("**Bidi-(Ent-)Lade-Strom setzen**")
46+
counter.set_raw_surplus_power_left()
47+
self.bidi.set_bidi()
4348
self.no_current.set_no_current()
4449
self.no_current.set_none_current()
4550
except Exception:
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import logging
2+
from control import data
3+
from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE
4+
from control.algorithm.filter_chargepoints import get_chargepoints_by_mode
5+
6+
log = logging.getLogger(__name__)
7+
8+
9+
class Bidi:
10+
def __init__(self):
11+
pass
12+
13+
def set_bidi(self):
14+
grid_counter = data.data.counter_all_data.get_evu_counter()
15+
log.debug(f"Nullpunktanpassung {grid_counter.data.set.surplus_power_left}W")
16+
zero_point_adjustment = grid_counter
17+
for mode_tuple in CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE:
18+
preferenced_cps = get_chargepoints_by_mode(mode_tuple)
19+
if preferenced_cps:
20+
log.info(
21+
f"Mode-Tuple {mode_tuple[0]} - {mode_tuple[1]} - {mode_tuple[2]}, Zähler {grid_counter.num}")
22+
while len(preferenced_cps):
23+
cp = preferenced_cps[0]
24+
zero_point_adjustment = grid_counter.data.set.surplus_power_left / len(preferenced_cps)
25+
log.debug(f"Nullpunktanpassung für LP{cp.num}: verbleibende Leistung {zero_point_adjustment}W")
26+
missing_currents = [zero_point_adjustment / cp.data.get.phases_in_use /
27+
230 for i in range(0, cp.data.get.phases_in_use)]
28+
missing_currents += [0] * (3 - len(missing_currents))
29+
if zero_point_adjustment > 0:
30+
if cp.data.set.charging_ev_data.charge_template.bidi_charging_allowed(
31+
cp.data.control_parameter.current_plan, cp.data.set.charging_ev_data.data.get.soc):
32+
for index in range(0, 3):
33+
missing_currents[index] = min(cp.data.control_parameter.required_current,
34+
missing_currents[index])
35+
else:
36+
log.info(f"LP{cp.num}: Nur bidirektional entladen erlaubt, da SoC-Limit erreicht.")
37+
missing_currents = [0, 0, 0]
38+
else:
39+
for index in range(0, 3):
40+
missing_currents[index] = cp.check_min_max_current(missing_currents[index],
41+
cp.data.get.phases_in_use)
42+
grid_counter.update_surplus_values_left(missing_currents, cp.data.get.voltages)
43+
cp.data.set.current = missing_currents[0]
44+
log.info(f"LP{cp.num}: Stromstärke {missing_currents}A")
45+
preferenced_cps.pop(0)

packages/control/algorithm/chargemodes.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,16 @@
1818
(Chargemode.ECO_CHARGING, Chargemode.PV_CHARGING, False),
1919
(Chargemode.PV_CHARGING, Chargemode.PV_CHARGING, True),
2020
(Chargemode.PV_CHARGING, Chargemode.PV_CHARGING, False),
21+
# niedrigere Priorität soll nachrangig geladen, aber zuerst entladen werden
22+
(Chargemode.SCHEDULED_CHARGING, Chargemode.BIDI_CHARGING, False),
23+
(Chargemode.SCHEDULED_CHARGING, Chargemode.BIDI_CHARGING, True),
2124
(None, Chargemode.STOP, True),
2225
(None, Chargemode.STOP, False))
2326

2427
CONSIDERED_CHARGE_MODES_SURPLUS = CHARGEMODES[0:2] + CHARGEMODES[6:16]
2528
CONSIDERED_CHARGE_MODES_PV_ONLY = CHARGEMODES[10:16]
2629
CONSIDERED_CHARGE_MODES_ADDITIONAL_CURRENT = CHARGEMODES[0:10]
27-
CONSIDERED_CHARGE_MODES_MIN_CURRENT = CHARGEMODES[0:-1]
28-
CONSIDERED_CHARGE_MODES_NO_CURRENT = CHARGEMODES[16:18]
30+
CONSIDERED_CHARGE_MODES_MIN_CURRENT = CHARGEMODES[0:-4]
31+
CONSIDERED_CHARGE_MODES_NO_CURRENT = CHARGEMODES[18:20]
32+
CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE = CHARGEMODES[16:18]
2933
CONSIDERED_CHARGE_MODES_CHARGING = CHARGEMODES[0:16]
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import pytest
2+
from unittest.mock import Mock
3+
4+
from control import data
5+
from control.algorithm.algorithm import Algorithm
6+
from control.chargemode import Chargemode
7+
8+
9+
@pytest.fixture()
10+
def bidi_cps():
11+
def _setup(*cps):
12+
for cp in cps:
13+
data.data.cp_data[cp].data.get.max_discharge_power = -11000
14+
data.data.cp_data[cp].data.get.max_charge_power = 11000
15+
data.data.cp_data[cp].data.get.phases_in_use = 3
16+
control_parameter = data.data.cp_data[cp].data.control_parameter
17+
control_parameter.min_current = data.data.cp_data[cp].data.set.charging_ev_data.ev_template.data.min_current
18+
control_parameter.phases = 3
19+
control_parameter.required_currents = [16]*3
20+
control_parameter.required_current = 16
21+
control_parameter.chargemode = Chargemode.SCHEDULED_CHARGING
22+
control_parameter.submode = Chargemode.BIDI_CHARGING
23+
return _setup
24+
25+
26+
@pytest.mark.parametrize("grid_power, expected_current",
27+
[pytest.param(-2000, 2.898550724637681, id="bidi charge"),
28+
pytest.param(2000, -2.898550724637681, id="bidi discharge")])
29+
def test_cp3_bidi(grid_power: float, expected_current: float, bidi_cps, all_cp_not_charging, monkeypatch):
30+
# setup
31+
bidi_cps("cp3")
32+
data.data.counter_data["counter0"].data.get.power = grid_power
33+
return_mock = Mock(reurn_value=True)
34+
monkeypatch.setattr(
35+
data.data.cp_data["cp3"].data.set.charging_ev_data.charge_template, "bidi_charging_allowed", return_mock)
36+
37+
# execution
38+
Algorithm().calc_current()
39+
40+
# evaluation
41+
assert data.data.cp_data["cp3"].data.set.current == expected_current
42+
assert data.data.cp_data["cp4"].data.set.current == 0
43+
assert data.data.cp_data["cp5"].data.set.current == 0
44+
assert data.data.counter_data["counter0"].data.set.surplus_power_left == 0
45+
46+
47+
def test_cp3_cp4_bidi_discharge(bidi_cps, all_cp_not_charging, monkeypatch):
48+
# setup
49+
bidi_cps("cp3", "cp4")
50+
data.data.counter_data["counter0"].data.get.power = 4000
51+
52+
# execution
53+
Algorithm().calc_current()
54+
55+
# evaluation
56+
assert data.data.cp_data["cp3"].data.set.current == -2.898550724637681
57+
assert data.data.cp_data["cp4"].data.set.current == -2.898550724637681
58+
assert data.data.cp_data["cp5"].data.set.current == 0
59+
assert data.data.counter_data["counter0"].data.set.surplus_power_left == 0

packages/control/algorithm/integration_test/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from control.bat import Bat
88
from control.bat_all import BatAll
99
from control.chargepoint.chargepoint import Chargepoint
10+
from control.chargepoint.chargepoint_template import CpTemplate
1011
from control.counter_all import CounterAll
1112
from control.counter import Counter
1213
from control.ev.ev import Ev
@@ -24,6 +25,7 @@ def data_() -> None:
2425
"cp4": Chargepoint(4, None),
2526
"cp5": Chargepoint(5, None)}
2627
for i in range(3, 6):
28+
data.data.cp_data[f"cp{i}"].template = CpTemplate()
2729
data.data.cp_data[f"cp{i}"].data.config.phase_1 = i-2
2830
data.data.cp_data[f"cp{i}"].data.set.charging_ev = i
2931
data.data.cp_data[f"cp{i}"].data.set.charging_ev_data = Ev(i)

packages/control/algorithm/integration_test/pv_charging_test.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ def test_start_pv_delay(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatch)
133133
assert data.data.cp_data[
134134
"cp5"].data.control_parameter.timestamp_switch_on_off == 1652683252.0
135135
assert data.data.counter_data["counter0"].data.set.raw_power_left == 31975
136-
assert data.data.counter_data["counter0"].data.set.surplus_power_left == 10090.0
136+
assert data.data.counter_data["counter0"].data.set.surplus_power_left == -690
137137
assert data.data.counter_data["counter0"].data.set.reserved_surplus == 9000
138138

139139

@@ -170,7 +170,7 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc
170170
assert data.data.cp_data[
171171
"cp5"].data.control_parameter.timestamp_switch_on_off is None
172172
assert data.data.counter_data["counter0"].data.set.raw_power_left == 24300
173-
assert data.data.counter_data["counter0"].data.set.surplus_power_left == 2415
173+
assert data.data.counter_data["counter0"].data.set.surplus_power_left == -690
174174
assert data.data.counter_data["counter0"].data.set.reserved_surplus == 0
175175

176176

@@ -184,7 +184,7 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc
184184
expected_current_cp4=8,
185185
expected_current_cp5=8,
186186
expected_raw_power_left=34820,
187-
expected_surplus_power_left=6035.0,
187+
expected_surplus_power_left=1090,
188188
expected_reserved_surplus=0,
189189
expected_released_surplus=0),
190190
ParamsSurplus(name="reduce current",
@@ -196,7 +196,7 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc
196196
expected_current_cp4=7.8731884057971016,
197197
expected_current_cp5=7.8731884057971016,
198198
expected_raw_power_left=24470,
199-
expected_surplus_power_left=0,
199+
expected_surplus_power_left=1090,
200200
expected_reserved_surplus=0,
201201
expected_released_surplus=0),
202202
ParamsSurplus(name="switch off delay for two of three charging",
@@ -208,7 +208,7 @@ def test_pv_delay_expired(all_cp_pv_charging_3p, all_cp_not_charging, monkeypatc
208208
expected_current_cp4=6,
209209
expected_current_cp5=6,
210210
expected_raw_power_left=5635,
211-
expected_surplus_power_left=-16250.0,
211+
expected_surplus_power_left=-8200,
212212
expected_reserved_surplus=0,
213213
expected_released_surplus=11040),
214214
]
@@ -247,7 +247,7 @@ def test_surplus(params: ParamsSurplus, all_cp_pv_charging_3p, all_cp_charging_3
247247
expected_current_cp4=6,
248248
expected_current_cp5=6,
249249
expected_raw_power_left=17400,
250-
expected_surplus_power_left=-4485,
250+
expected_surplus_power_left=-690,
251251
expected_reserved_surplus=0,
252252
expected_released_surplus=0),
253253
ParamsPhaseSwitch(name="phase switch 1p->3p",
@@ -261,7 +261,7 @@ def test_surplus(params: ParamsSurplus, all_cp_pv_charging_3p, all_cp_charging_3
261261
expected_current_cp4=6,
262262
expected_current_cp5=6,
263263
expected_raw_power_left=37520.0,
264-
expected_surplus_power_left=10575.0,
264+
expected_surplus_power_left=3000,
265265
expected_reserved_surplus=0,
266266
expected_released_surplus=0)
267267
]

packages/control/algorithm/surplus_controlled.py

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
from control import data
55
from control.algorithm import common
6-
from control.algorithm.chargemodes import CONSIDERED_CHARGE_MODES_PV_ONLY, CONSIDERED_CHARGE_MODES_SURPLUS
6+
from control.algorithm.chargemodes import (CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE, CONSIDERED_CHARGE_MODES_PV_ONLY,
7+
CONSIDERED_CHARGE_MODES_SURPLUS)
78
from control.algorithm.filter_chargepoints import (get_chargepoints_by_chargemodes,
89
get_chargepoints_by_mode_and_counter,
910
get_preferenced_chargepoint_charging)
@@ -76,7 +77,7 @@ def _set(self,
7677

7778
current = common.get_current_to_set(cp.data.set.current, current, cp.data.set.target_current)
7879
self._set_loadmangement_message(current, limit, cp)
79-
limited_current = self._limit_adjust_current(cp, current)
80+
limited_current = limit_adjust_current(cp, current)
8081
common.set_current_counterdiff(
8182
cp.data.control_parameter.min_current,
8283
limited_current,
@@ -105,35 +106,6 @@ def filter_by_feed_in_limit(self, chargepoints: List[Chargepoint]) -> Tuple[List
105106
pv_charging.feed_in_limit is False, chargepoints))
106107
return cp_with_feed_in, cp_without_feed_in
107108

108-
# tested
109-
def _limit_adjust_current(self, chargepoint: Chargepoint, new_current: float) -> float:
110-
if chargepoint.template.data.charging_type == ChargingType.AC.value:
111-
MAX_CURRENT = 5
112-
else:
113-
MAX_CURRENT = 30
114-
msg = None
115-
nominal_difference = chargepoint.data.set.charging_ev_data.ev_template.data.nominal_difference
116-
if chargepoint.chargemode_changed or chargepoint.data.get.charge_state is False:
117-
return new_current
118-
else:
119-
# Um max. +/- 5A pro Zyklus regeln
120-
if (-MAX_CURRENT-nominal_difference
121-
< new_current - get_medium_charging_current(chargepoint.data.get.currents)
122-
< MAX_CURRENT+nominal_difference):
123-
current = new_current
124-
else:
125-
if new_current < get_medium_charging_current(chargepoint.data.get.currents):
126-
current = get_medium_charging_current(chargepoint.data.get.currents) - MAX_CURRENT
127-
msg = f"Es darf um max {MAX_CURRENT}A unter den aktuell genutzten Strom geregelt werden."
128-
129-
else:
130-
current = get_medium_charging_current(chargepoint.data.get.currents) + MAX_CURRENT
131-
msg = f"Es darf um max {MAX_CURRENT}A über den aktuell genutzten Strom geregelt werden."
132-
chargepoint.set_state_and_log(msg)
133-
return max(current,
134-
chargepoint.data.control_parameter.min_current,
135-
chargepoint.data.set.target_current)
136-
137109
def _fix_deviating_evse_current(self, chargepoint: Chargepoint) -> float:
138110
"""Wenn Autos nicht die volle Ladeleistung nutzen, wird unnötig eingespeist. Dann kann um den noch nicht
139111
genutzten Soll-Strom hochgeregelt werden. Wenn Fahrzeuge entgegen der Norm mehr Ladeleistung beziehen, als
@@ -182,7 +154,8 @@ def phase_switch_necessary() -> bool:
182154
log.exception(f"Fehler in der PV-gesteuerten Ladung bei {cp.num}")
183155

184156
def set_required_current_to_max(self) -> None:
185-
for cp in get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_SURPLUS):
157+
for cp in get_chargepoints_by_chargemodes(CONSIDERED_CHARGE_MODES_SURPLUS +
158+
CONSIDERED_CHARGE_MODES_BIDI_DISCHARGE):
186159
try:
187160
charging_ev_data = cp.data.set.charging_ev_data
188161
required_currents = cp.data.control_parameter.required_currents
@@ -206,3 +179,33 @@ def set_required_current_to_max(self) -> None:
206179
control_parameter.required_current = max_current
207180
except Exception:
208181
log.exception(f"Fehler in der PV-gesteuerten Ladung bei {cp.num}")
182+
183+
184+
# tested
185+
def limit_adjust_current(chargepoint: Chargepoint, new_current: float) -> float:
186+
if chargepoint.template.data.charging_type == ChargingType.AC.value:
187+
MAX_CURRENT = 5
188+
else:
189+
MAX_CURRENT = 30
190+
msg = None
191+
nominal_difference = chargepoint.data.set.charging_ev_data.ev_template.data.nominal_difference
192+
if chargepoint.chargemode_changed or chargepoint.data.get.charge_state is False:
193+
return new_current
194+
else:
195+
# Um max. +/- 5A pro Zyklus regeln
196+
if (-MAX_CURRENT-nominal_difference
197+
< new_current - get_medium_charging_current(chargepoint.data.get.currents)
198+
< MAX_CURRENT+nominal_difference):
199+
current = new_current
200+
else:
201+
if new_current < get_medium_charging_current(chargepoint.data.get.currents):
202+
current = get_medium_charging_current(chargepoint.data.get.currents) - MAX_CURRENT
203+
msg = f"Es darf um max {MAX_CURRENT}A unter den aktuell genutzten Strom geregelt werden."
204+
205+
else:
206+
current = get_medium_charging_current(chargepoint.data.get.currents) + MAX_CURRENT
207+
msg = f"Es darf um max {MAX_CURRENT}A über den aktuell genutzten Strom geregelt werden."
208+
chargepoint.set_state_and_log(msg)
209+
return max(current,
210+
chargepoint.data.control_parameter.min_current,
211+
chargepoint.data.set.target_current)

packages/control/algorithm/surplus_controlled_test.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
from control import data
66
from control.algorithm import surplus_controlled
77
from control.algorithm.filter_chargepoints import get_chargepoints_by_chargemodes
8-
from control.algorithm.surplus_controlled import CONSIDERED_CHARGE_MODES_PV_ONLY, SurplusControlled
8+
from control.algorithm.surplus_controlled import (CONSIDERED_CHARGE_MODES_PV_ONLY, SurplusControlled,
9+
limit_adjust_current)
910
from control.chargemode import Chargemode
1011
from control.chargepoint.chargepoint import Chargepoint, ChargepointData
1112
from control.chargepoint.chargepoint_data import Get, Set
@@ -66,7 +67,7 @@ def test_limit_adjust_current(new_current: float, expected_current: float, monke
6667
monkeypatch.setattr(Chargepoint, "set_state_and_log", Mock())
6768

6869
# execution
69-
current = SurplusControlled()._limit_adjust_current(cp, new_current)
70+
current = limit_adjust_current(cp, new_current)
7071
# evaluation
7172
assert current == expected_current
7273

packages/control/bat_all.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ def _get_charging_power_left(self):
209209
else:
210210
charging_power_left = 0
211211
self.data.set.regulate_up = True if self.data.get.soc < 100 else False
212+
# ev wird nach Speicher geladen
212213
elif config.bat_mode == BatConsiderationMode.EV_MODE.value:
213214
# Speicher sollte weder ge- noch entladen werden.
214215
charging_power_left = self.data.get.power

packages/control/chargemode.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ class Chargemode(Enum):
77
INSTANT_CHARGING = "instant_charging"
88
PV_CHARGING = "pv_charging"
99
ECO_CHARGING = "eco_charging"
10+
BIDI_CHARGING = "bidi_charging"
1011
STOP = "stop"

0 commit comments

Comments
 (0)