Skip to content

Commit

Permalink
Feature dc adapter (#1864)
Browse files Browse the repository at this point in the history
* DC charging

* fix test

* fix max current

* fix rebase

* DC charging

* draft

* draft

* fixes

* naming

* update config

* fix test

* fix test

* undo

* fix update config

* fix

* remove old code

* fix configuration.json
  • Loading branch information
LKuemmel authored Sep 3, 2024
1 parent 0fc7b18 commit 14cfd69
Show file tree
Hide file tree
Showing 45 changed files with 660 additions and 185 deletions.
8 changes: 7 additions & 1 deletion packages/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
from control.counter_all import CounterAll
from control.pv import Pv, PvData
from control.pv import Get as PvGet
from helpermodules import pub, timecheck
from helpermodules import hardware_configuration, pub, timecheck


@pytest.fixture(autouse=True)
def mock_open_file(monkeypatch) -> None:
mock_config = Mock(return_value={"dc_charging": False, "openwb-version": 1, "max_c_socket": 32})
monkeypatch.setattr(hardware_configuration, "_read_configuration", mock_config)


@pytest.fixture(autouse=True)
Expand Down
11 changes: 8 additions & 3 deletions packages/control/algorithm/additional_current.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ def set_additional_current(self) -> None:
cp = preferenced_chargepoints[0]
missing_currents, counts = common.get_missing_currents_left(preferenced_chargepoints)
available_currents, limit = Loadmanagement().get_available_currents(missing_currents, counter)
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)
common.set_current_counterdiff(
current - cp.data.set.charging_ev_data.ev_template.data.min_current,
cp.data.control_parameter.min_current,
current,
cp)
preferenced_chargepoints.pop(0)
Expand All @@ -50,9 +52,12 @@ def _set_loadmangement_message(self,
chargepoint: Chargepoint,
counter: Counter) -> 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}"
f" required currents {chargepoint.data.control_parameter.required_currents}")
if (current != max(chargepoint.data.set.target_current, chargepoint.data.set.current or 0) and
# Strom erreicht nicht die vorgegebene Stromstärke
current != max(
chargepoint.data.control_parameter.required_currents)):
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))}")
48 changes: 31 additions & 17 deletions packages/control/algorithm/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from control.chargemode import Chargemode
from control.chargepoint.chargepoint import Chargepoint
from control.counter import Counter
from helpermodules.timecheck import check_timestamp
from modules.common.component_type import ComponentType

log = logging.getLogger(__name__)
Expand All @@ -29,6 +30,8 @@
(None, Chargemode.STOP, True),
(None, Chargemode.STOP, False))

LESS_CHARGING_TIMEOUT = 60

# tested


Expand Down Expand Up @@ -62,21 +65,27 @@ def mode_and_counter_generator(chargemodes: List) -> Iterable[Tuple[Tuple[Option
def get_min_current(chargepoint: Chargepoint) -> Tuple[List[float], List[int]]:
min_currents = [0.0]*3
counts = [0]*3
charging_ev_data = chargepoint.data.set.charging_ev_data
required_currents = chargepoint.data.control_parameter.required_currents
for i in range(3):
if required_currents[i] != 0:
counts[i] += 1
min_currents[i] = charging_ev_data.ev_template.data.min_current
min_currents[i] = chargepoint.data.control_parameter.min_current
else:
min_currents[i] = 0
return min_currents, counts

# tested


def set_current_counterdiff(diff: float, current: float, chargepoint: Chargepoint, surplus: bool = False) -> None:
def set_current_counterdiff(diff_curent: float,
current: float,
chargepoint: Chargepoint,
surplus: bool = False) -> None:
required_currents = chargepoint.data.control_parameter.required_currents
considered_current = consider_less_charging_chargepoint_in_loadmanagement(
chargepoint, current)
# gar nicht ladende Autos?
diff = max(considered_current - diff_curent, 0)
diffs = [diff if required_currents[i] != 0 else 0 for i in range(3)]
if max(diffs) > 0:
counters = data.data.counter_all_data.get_counters_to_check(chargepoint.num)
Expand Down Expand Up @@ -129,21 +138,20 @@ def update_raw_data(preferenced_chargepoints: List[Chargepoint],
"""alle CP, die schon einen Sollstrom haben, wieder rausrechnen, da dieser neu gesetzt wird
und die neue Differenz bei den Zählern eingetragen wird."""
for chargepoint in preferenced_chargepoints:
if consider_not_charging_chargepoint_in_loadmanagement(chargepoint):
continue
charging_ev_data = chargepoint.data.set.charging_ev_data
required_currents = chargepoint.data.control_parameter.required_currents
max_target_set_current = max(chargepoint.data.set.target_current, chargepoint.data.set.current or 0)
max_target_set_current = consider_less_charging_chargepoint_in_loadmanagement(
chargepoint, max_target_set_current)

if diff_to_zero is False:
if charging_ev_data.ev_template.data.min_current < max_target_set_current:
diffs = [charging_ev_data.ev_template.data.min_current -
if chargepoint.data.control_parameter.min_current < max_target_set_current:
diffs = [chargepoint.data.control_parameter.min_current -
max_target_set_current if required_currents[i] != 0 else 0 for i in range(3)]
else:
continue
else:
if charging_ev_data.ev_template.data.min_current <= max_target_set_current:
diffs = [-charging_ev_data.ev_template.data.min_current if required_currents[i]
if chargepoint.data.control_parameter.min_current <= max_target_set_current:
diffs = [-chargepoint.data.control_parameter.min_current if required_currents[i]
!= 0 else 0 for i in range(3)]
else:
continue
Expand All @@ -155,26 +163,32 @@ def update_raw_data(preferenced_chargepoints: List[Chargepoint],
data.data.counter_data[counter].update_values_left(diffs)


def consider_not_charging_chargepoint_in_loadmanagement(cp: Chargepoint) -> bool:
# tested
return data.data.counter_all_data.data.config.reserve_for_not_charging is False and max(cp.data.get.currents) == 0

def consider_less_charging_chargepoint_in_loadmanagement(cp: Chargepoint, set_current: float) -> bool:
if (data.data.counter_all_data.data.config.consider_less_charging and
((set_current -
cp.data.set.charging_ev_data.ev_template.data.nominal_difference) > max(cp.data.get.currents) and
cp.data.control_parameter.timestamp_charge_start is not None and
check_timestamp(cp.data.control_parameter.timestamp_charge_start, LESS_CHARGING_TIMEOUT) is False)):
log.debug(
f"LP {cp.num} lädt deutlich unter dem Sollstrom und wird nur mit {cp.data.get.currents}A berücksichtigt.")
return max(cp.data.get.currents)
else:
return set_current
# tested


def get_missing_currents_left(preferenced_chargepoints: List[Chargepoint]) -> Tuple[List[float], List[int]]:
missing_currents = [0.0]*3
counts = [0]*3
for chargepoint in preferenced_chargepoints:
charging_ev_data = chargepoint.data.set.charging_ev_data
required_currents = chargepoint.data.control_parameter.required_currents
for i in range(0, 3):
if required_currents[i] != 0:
counts[i] += 1
try:
missing_currents[i] += required_currents[i] - charging_ev_data.ev_template.data.min_current
missing_currents[i] += required_currents[i] - chargepoint.data.control_parameter.min_current
except KeyError:
missing_currents[i] += max(required_currents) - charging_ev_data.ev_template.data.min_current
missing_currents[i] += max(required_currents) - chargepoint.data.control_parameter.min_current
else:
missing_currents[i] += 0
return missing_currents, counts
Expand Down
32 changes: 16 additions & 16 deletions packages/control/algorithm/common_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@ def test_reset_current(set_current: int, expected_current: int):
@pytest.mark.parametrize(
"diff, required_currents, expected_set_current, expected_diffs",
[
pytest.param(2, [10, 0, 0], 8, [2, 0, 0], id="set diff one phase"),
pytest.param(2, [12]*3, 8, [2]*3, id="set diff three phases"),
pytest.param(8, [8]*3, 8, [8]*3, id="set min current three phases"),
pytest.param(0, [8]*3, 8, [0]*3, id="min current is already set, three phases"),
pytest.param(10, [10, 0, 0], 10, [2, 0, 0], id="set diff one phase"),
pytest.param(10, [12]*3, 10, [2]*3, id="set diff three phases"),
pytest.param(8, [8]*3, 8, [0]*3, id="min current is already set, three phases"),
])
def test_set_current_counterdiff(diff: float,
required_currents: List[float],
Expand All @@ -55,11 +54,11 @@ def test_set_current_counterdiff(diff: float,
data.data.counter_data = {"cp0": Mock(spec=Counter), "cp6": Mock(spec=Counter)}

# evaluation
common.set_current_counterdiff(diff, 8, cp)
common.set_current_counterdiff(8, diff, cp)

# assertion
assert cp.data.set.current == expected_set_current
if diff != 0:
if max(expected_diffs) != 0:
assert data.data._counter_data['cp0'].update_values_left.call_args_list[0][0][0] == expected_diffs
assert data.data._counter_data['cp6'].update_values_left.call_args_list[0][0][0] == expected_diffs

Expand Down Expand Up @@ -152,23 +151,24 @@ def setup_cp(num: int, required_currents) -> Chargepoint:


@pytest.mark.parametrize(
"reserve_for_not_charging, get_currents, expected_considered",
"consider_less_charging, get_currents, expected_considered",
[
pytest.param(True, [0]*3, False, id="reserve_for_not_charging active"),
pytest.param(True, [6]*3, False, id="reserve_for_not_charging active"),
pytest.param(False, [0]*3, True, id="not charging"),
pytest.param(False, [6]*3, False, id="charging"),
pytest.param(True, [6]*3, 6, id="consider_less_charging active, charging less"),
pytest.param(True, [10]*3, 10, id="consider_less_charging active, charging with set current"),
pytest.param(False, [0]*3, 10, id="consider_less_charging inactive"),
])
def test_consider_not_charging_chargepoint_in_loadmanagement(reserve_for_not_charging: bool,
get_currents: List[float],
expected_considered: bool):
def test_consider_less_charging_chargepoint_in_loadmanagement(consider_less_charging: bool,
get_currents: List[float],
expected_considered: bool):
# setup
cp = Chargepoint(4, None)
cp.data.get.currents = get_currents
data.data.counter_all_data.data.config.reserve_for_not_charging = reserve_for_not_charging
cp.data.set.current = 10
cp.data.control_parameter.timestamp_charge_start = 1652683152
data.data.counter_all_data.data.config.consider_less_charging = consider_less_charging

# evaluation
considered = common.consider_not_charging_chargepoint_in_loadmanagement(cp)
considered = common.consider_less_charging_chargepoint_in_loadmanagement(cp, 10)

# assertion
assert considered == expected_considered
3 changes: 2 additions & 1 deletion packages/control/algorithm/integration_test/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def data_() -> None:
data.data.cp_data[f"cp{i}"].data.set.plug_time = f"12/01/2022, 15:0{i}:11"
data.data.cp_data[f"cp{i}"].data.set.charging_ev_data.ev_template.data.nominal_difference = 2
data.data.cp_data["cp3"].data.set.charging_ev_data.ev_template.data.min_current = 10
data.data.cp_data["cp3"].data.control_parameter.min_current = 10
data.data.bat_data.update({"bat2": Bat(2), "all": BatAll()})
data.data.pv_data.update({"pv1": Pv(1)})
data.data.counter_data.update({
Expand All @@ -46,7 +47,7 @@ def data_() -> None:
data.data.counter_data["counter6"].data.config.max_total_power = 11000
data.data.counter_all_data = CounterAll()
data.data.counter_all_data.data.get.hierarchy = NESTED_HIERARCHY
data.data.counter_all_data.data.config.reserve_for_not_charging = True
data.data.counter_all_data.data.config.consider_less_charging = True


@dataclass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
def all_cp_instant_charging_1p():
for i in range(3, 6):
control_parameter = data.data.cp_data[f"cp{i}"].data.control_parameter
control_parameter.min_current = data.data.cp_data[
f"cp{i}"].data.set.charging_ev_data.ev_template.data.min_current
control_parameter.required_currents = [0]*3
control_parameter.required_currents[i-3] = 16
control_parameter.required_current = 16
Expand All @@ -34,6 +36,8 @@ def all_cp_charging_1p():
def all_cp_instant_charging_3p():
for i in range(3, 6):
control_parameter = data.data.cp_data[f"cp{i}"].data.control_parameter
control_parameter.min_current = data.data.cp_data[
f"cp{i}"].data.set.charging_ev_data.ev_template.data.min_current
control_parameter.required_currents = [16]*3
control_parameter.required_current = 16
control_parameter.chargemode = Chargemode.INSTANT_CHARGING
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def all_cp_pv_charging_3p():
f"cp{i}"].data.set.charging_ev_data.ev_template.data.min_current
control_parameter.required_currents = [
data.data.cp_data[f"cp{i}"].data.set.charging_ev_data.ev_template.data.min_current]*3
control_parameter.min_current = data.data.cp_data[
f"cp{i}"].data.set.charging_ev_data.ev_template.data.min_current
control_parameter.chargemode = Chargemode.PV_CHARGING
control_parameter.submode = Chargemode.PV_CHARGING
control_parameter.phases = 3
Expand Down Expand Up @@ -52,6 +54,8 @@ def all_cp_pv_charging_1p():
for i in range(3, 6):
control_parameter = data.data.cp_data[f"cp{i}"].data.control_parameter
charging_ev_data = data.data.cp_data[f"cp{i}"].data.set.charging_ev_data
control_parameter.min_current = data.data.cp_data[
f"cp{i}"].data.set.charging_ev_data.ev_template.data.min_current
control_parameter.required_current = data.data.cp_data[
f"cp{i}"].data.set.charging_ev_data.ev_template.data.min_current
control_parameter.required_currents = [0]*3
Expand Down
26 changes: 9 additions & 17 deletions packages/control/algorithm/min_current.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,16 @@ def set_min_current(self) -> None:
cp, counts, available_currents, missing_currents)
current = common.get_current_to_set(
cp.data.set.current, available_for_cp, cp.data.set.target_current)
if common.consider_not_charging_chargepoint_in_loadmanagement(cp):
cp.data.set.current = cp.data.set.charging_ev_data.ev_template.data.min_current
log.debug(
f"LP{cp.num}: Stromstärke {cp.data.set.charging_ev_data.ev_template.data.min_current}"
"A. Zuteilung ohne Berücksichtigung im Lastmanagement, da kein Ladestart zu erwarten "
"ist und Reserve für nicht-ladende inaktiv.")
if current < cp.data.control_parameter.min_current:
common.set_current_counterdiff(-(cp.data.set.current or 0), 0, cp)
if limit:
cp.set_state_and_log(
f"Ladung kann nicht gestartet werden{limit.value.format(counter.num)}")
else:
if current < cp.data.set.charging_ev_data.ev_template.data.min_current:
common.set_current_counterdiff(-(cp.data.set.current or 0), 0, cp)
if limit:
cp.set_state_and_log(
f"Ladung kann nicht gestartet werden{limit.value.format(counter.num)}")
else:
common.set_current_counterdiff(
(cp.data.set.charging_ev_data.ev_template.data.min_current
- cp.data.set.target_current),
cp.data.set.charging_ev_data.ev_template.data.min_current,
cp)
common.set_current_counterdiff(
cp.data.set.target_current,
cp.data.control_parameter.min_current,
cp)
else:
cp.data.set.current = 0
preferenced_chargepoints.pop(0)
4 changes: 2 additions & 2 deletions packages/control/algorithm/surplus_controlled.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def _set(self,
limited_current = self._limit_adjust_current(cp, current)
limited_current = self._add_unused_evse_current(limited_current, cp)
common.set_current_counterdiff(
limited_current - cp.data.set.charging_ev_data.ev_template.data.min_current,
cp.data.control_parameter.min_current,
limited_current,
cp,
surplus=True)
Expand Down Expand Up @@ -107,7 +107,7 @@ def _limit_adjust_current(self, chargepoint: Chargepoint, new_current: float) ->
current = max(chargepoint.data.get.currents) + MAX_CURRENT
msg = "Es darf um max 5A über den aktuell genutzten Strom geregelt werden."
chargepoint.set_state_and_log(msg)
return max(current, chargepoint.data.set.charging_ev_data.ev_template.data.min_current)
return max(current, chargepoint.data.control_parameter.min_current)

def _add_unused_evse_current(self, limited_current, chargepoint: Chargepoint) -> float:
"""Wenn Autos nicht die volle Ladeleistung nutzen, wird unnötig eingespeist. Dann kann um den noch nicht
Expand Down
Loading

0 comments on commit 14cfd69

Please sign in to comment.