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
49 changes: 25 additions & 24 deletions packages/control/algorithm/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from control.algorithm.utils import get_medium_charging_current
from control.chargepoint.chargepoint import Chargepoint
from control.counter import Counter
from control.load import get_load_str
from control.load_protocol import Load
from helpermodules.phase_handling import voltages_mean
from helpermodules.timecheck import check_timestamp
Expand All @@ -25,8 +26,7 @@ def reset_current():
load.data.set.current = None
load.data.set.target_current = 0
except Exception:
log.exception(f"Fehler im Algorithmus-Modul für "
f"{'Ladepunkt' if isinstance(load, Chargepoint) else 'Verbraucher'}{load.num}")
log.exception(f"Fehler im Algorithmus-Modul für {get_load_str(load)}")


def reset_current_by_chargemode(chargemodes: Tuple[Tuple[Optional[str], str]]) -> None:
Expand Down Expand Up @@ -66,11 +66,8 @@ def set_current_counterdiff(diff_current: float,
load: Load,
surplus: bool = False) -> None:
required_currents = load.data.control_parameter.required_currents
if isinstance(load, Chargepoint):
considered_current = consider_less_charging_chargepoint_in_loadmanagement(
load, current)
else:
considered_current = current
considered_current = consider_less_charging_chargepoint_in_loadmanagement(
load, current)
# gar nicht ladende Autos?
diff = max(considered_current - diff_current, 0)
diffs = [diff if required_currents[i] != 0 else 0 for i in range(3)]
Expand All @@ -90,7 +87,7 @@ def set_current_counterdiff(diff_current: float,
data.data.io_actions.set_limit_loadmanager(sum(diffs)*230)

load.data.set.current = current
log.info(f"{'LP' if isinstance(load, Chargepoint) else 'Verbraucher'} {load.num}: Stromstärke {current}A")
log.info(f"{get_load_str(load)}: Stromstärke {current}A")

# tested

Expand Down Expand Up @@ -136,9 +133,8 @@ def update_raw_data(preferenced_loads: List[Load],
for load in preferenced_loads:
required_currents = load.data.control_parameter.required_currents
max_target_set_current = max(load.data.set.target_current, load.data.set.current or 0)
if isinstance(load, Chargepoint):
max_target_set_current = consider_less_charging_chargepoint_in_loadmanagement(
load, max_target_set_current)
max_target_set_current = consider_less_charging_chargepoint_in_loadmanagement(
load, max_target_set_current)

if diff_to_zero is False:
if load.data.control_parameter.min_current < max_target_set_current:
Expand Down Expand Up @@ -169,18 +165,24 @@ def update_raw_data(preferenced_loads: List[Load],
data.data.io_actions.set_limit_loadmanager(sum(diffs)*230)


def consider_less_charging_chargepoint_in_loadmanagement(cp: Chargepoint, set_current: float) -> bool:
if (data.data.counter_all_data.data.config.consider_less_charging is False and
((set_current -
cp.data.set.charging_ev_data.ev_template.data.nominal_difference) > get_medium_charging_current(
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 get_medium_charging_current(cp.data.get.currents)
def consider_less_charging_chargepoint_in_loadmanagement(load: Load, set_current: float) -> float:
if isinstance(load, Chargepoint):
expected_current = set_current - load.data.set.charging_ev_data.ev_template.data.nominal_difference
else:
return set_current
expected_current = set_current - 1
expected_current = max(expected_current, 0)
Comment on lines +169 to +173
if data.data.counter_all_data.data.config.consider_less_charging is False:
if expected_current >= get_medium_charging_current(load.data.get.currents):
if load.data.control_parameter.timestamp_charge_start is not None:
if check_timestamp(load.data.control_parameter.timestamp_charge_start, LESS_CHARGING_TIMEOUT) is False:
log.debug(f"{get_load_str(load)} lädt deutlich unter dem Sollstrom und "
f"wird nur mit {load.data.get.currents}A berücksichtigt.")
return get_medium_charging_current(load.data.get.currents)
elif load.data.set.current_prev != 0:
log.debug(
f"{get_load_str(load)} lädt nicht und wird nur mit {load.data.get.currents}A berücksichtigt.")
return get_medium_charging_current(load.data.get.currents)
return set_current
# tested


Expand Down Expand Up @@ -209,5 +211,4 @@ def reset_current_to_target_current():
try:
load.data.set.target_current = load.data.set.current
except Exception:
log.exception(f"Fehler im Algorithmus-Modul für "
f"{'Ladepunkt' if isinstance(load, Chargepoint) else 'Verbraucher'}{load.num}")
log.exception(f"Fehler im Algorithmus-Modul für {get_load_str(load)}")
30 changes: 30 additions & 0 deletions packages/control/algorithm/common_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from control.ev.ev import Ev
from control.counter import Counter
from control.counter_all.counter_all import CounterAll
from control.consumer.consumer import Consumer
from control.io_device import IoActions


Expand Down Expand Up @@ -176,3 +177,32 @@ def test_consider_less_charging_chargepoint_in_loadmanagement(consider_less_char

# assertion
assert considered == expected_considered


@pytest.mark.parametrize(
"consider_less_charging, set_current, get_currents, current_prev, expected_considered",
[
pytest.param(False, 10, [2]*3, 1, 2,
id="consumer less charging considered via timestamp none and current_prev"),
pytest.param(False, 10, [10]*3, 1, 10,
id="consumer expected_current threshold not reached"),
pytest.param(True, 10, [0]*3, 10, 10,
id="consider_less_charging"),
])
def test_consider_less_charging_consumer_in_loadmanagement(consider_less_charging: bool,
set_current: float,
get_currents: List[float],
current_prev: float,
expected_considered: float):
# setup
consumer = Consumer(7)
consumer.data.get.currents = get_currents
consumer.data.set.current_prev = current_prev
consumer.data.control_parameter.timestamp_charge_start = 1652683152
data.data.counter_all_data.data.config.consider_less_charging = consider_less_charging

# evaluation
considered = common.consider_less_charging_chargepoint_in_loadmanagement(consumer, set_current)

# assertion
assert considered == expected_considered
9 changes: 4 additions & 5 deletions packages/control/algorithm/filter_chargepoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

from control import data
from control.chargemode import Chargemode
from control.chargepoint.chargepoint import Chargepoint
from control.consumer.consumer import Consumer
from control.consumer.usage import ConsumerUsage
from control.load import get_load_str
from control.load_protocol import Load

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -84,12 +84,11 @@ def get_preferenced_load_charging(
valid_group: List[Load] = []
for load in group:
if load.data.set.target_current == 0:
log.info(f"{'LP' if isinstance(load, Chargepoint) else 'Verbraucher'} {load.num}: "
log.info(f"{get_load_str(load)}: "
f"Keine Zuteilung des Mindeststroms, daher keine weitere Berücksichtigung")
preferenced_loads_without_set_current.append(load)
elif load.data.get.charge_state is False:
log.info(f"{'LP' if isinstance(load, Chargepoint) else 'Verbraucher'} {load.num}: "
f"Lädt nicht, daher keine weitere Berücksichtigung")
log.info(f"{get_load_str(load)}: Lädt nicht, daher keine weitere Berücksichtigung")
preferenced_loads_without_set_current.append(load)
elif (isinstance(load, Consumer) and
load.data.usage.type in [ConsumerUsage.CONTINUOUS, ConsumerUsage.SUSPENDABLE_ONOFF]):
Expand All @@ -103,4 +102,4 @@ def get_preferenced_load_charging(


def filtered_loads_to_str(loads: List[Load]) -> str:
return ", ".join([f"{'LP' if isinstance(load, Chargepoint) else 'Verbraucher'}{load.num}" for load in loads])
return ", ".join([get_load_str(load) for load in loads])
9 changes: 0 additions & 9 deletions packages/control/chargepoint/chargepoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,15 +591,6 @@ def set_required_currents(self, required_current: float) -> None:
self.data.set.required_power = sum(
[c * v for c, v in zip(control_parameter.required_currents, self.data.get.voltages)])

def set_timestamp_charge_start(self):
# Beim Ladestart Timer laufen lassen, manche Fahrzeuge brauchen sehr lange.
# Nach dem Algorithmus setzen, sonst steht set current noch nicht fest.
if self.data.control_parameter.timestamp_charge_start is None:
if self.data.set.current_prev == 0 and self.data.set.current != 0:
self.data.control_parameter.timestamp_charge_start = create_timestamp()
elif self.data.set.current == 0:
self.data.control_parameter.timestamp_charge_start = None

def set_chargemode_changed(self, submode: Chargemode) -> None:
if ((submode == Chargemode.TIME_CHARGING and
self.data.control_parameter.chargemode != Chargemode.TIME_CHARGING) or
Expand Down
30 changes: 0 additions & 30 deletions packages/control/chargepoint/chargepoint_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import logging

from control import data
from control.chargepoint.chargepoint_state import ChargepointState
from helpermodules.constants import NO_ERROR


Expand Down Expand Up @@ -37,35 +36,6 @@ def all_chargepoint_data_factory() -> AllChargepointData:
class AllChargepoints:
data: AllChargepointData = field(default_factory=all_chargepoint_data_factory)

def no_charge(self):
""" Wenn keine EV angesteckt sind oder keine Verzögerungen aktiv sind, werden die Algorithmus-Werte
zurückgesetzt.
(dient der Robustheit)
"""
try:
for cp in data.data.cp_data:
try:
chargepoint = data.data.cp_data[cp]
# Kein EV angesteckt
control_parameter = chargepoint.data.control_parameter
if (not chargepoint.data.get.plug_state or
# Kein EV, das Laden soll
# Kein EV, das auf das Ablaufen der Einschalt- oder Phasenumschaltverzögerung wartet
(control_parameter.state != ChargepointState.PERFORMING_PHASE_SWITCH and
control_parameter.state != ChargepointState.PHASE_SWITCH_DELAY and
control_parameter.state != ChargepointState.SWITCH_OFF_DELAY and
control_parameter.state != ChargepointState.SWITCH_ON_DELAY and
control_parameter.state != ChargepointState.NO_CHARGING_ALLOWED)):
continue
else:
break
except Exception:
log.exception("Fehler in der allgemeinen Ladepunkt-Klasse für Ladepunkt "+cp)
else:
data.data.counter_all_data.get_evu_counter().reset_pv_data()
except Exception:
log.exception("Fehler in der allgemeinen Ladepunkt-Klasse")

def get_cp_sum(self):
""" ermittelt die aktuelle Leistung und Zählerstand von allen Ladepunkten.
"""
Expand Down
8 changes: 0 additions & 8 deletions packages/control/consumer/consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,6 @@ def process_on_time(self):
self.data.set.timestamp_wrote_last_on_time = now
self.data.set.on_time += now - self.data.set.timestamp_wrote_last_on_time
self.data.set.timestamp_wrote_last_on_time = now
if self.data.control_parameter.timestamp_charge_start is None:
self.data.control_parameter.timestamp_charge_start = timecheck.create_timestamp()
elif self.data.get.charge_state is False:
self.data.control_parameter.timestamp_charge_start = None

def reset_timestamp_start(self):
self.data.control_parameter.timestamp_charge_start = None

def reset_on_time(self):
self.data.set.on_time = 0
Expand Down Expand Up @@ -456,7 +449,6 @@ def reset_wait_for_start(self):
def midnight_handler(self):
self.reset_chargemode_at_midnight()
self.reset_wait_for_start()
self.reset_timestamp_start()
self.reset_on_time()

def reset_chargemode_at_midnight(self):
Expand Down
44 changes: 44 additions & 0 deletions packages/control/load.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@

import logging

from control import data
from control.chargepoint.chargepoint import Chargepoint
from control.chargepoint.chargepoint_state import ChargepointState
from control.load_protocol import Load


log = logging.getLogger(__name__)


def no_charge() -> None:
""" Wenn keine EV angesteckt sind oder keine Verzögerungen aktiv sind, werden die Algorithmus-Werte
zurückgesetzt.
(dient der Robustheit)
"""
try:
for load in list(data.data.cp_data.values()) + list(data.data.consumer_data.values()):
try:
control_parameter = load.data.control_parameter
# Kein EV, das Laden soll
# Kein EV, das auf das Ablaufen der Einschalt- oder Phasenumschaltverzögerung wartet
if (control_parameter.state != ChargepointState.PERFORMING_PHASE_SWITCH and
control_parameter.state != ChargepointState.PHASE_SWITCH_DELAY and
control_parameter.state != ChargepointState.SWITCH_OFF_DELAY and
control_parameter.state != ChargepointState.SWITCH_ON_DELAY and
control_parameter.state != ChargepointState.NO_CHARGING_ALLOWED):
continue
else:
break
except Exception:
log.exception("Fehler beim Bereinigen der Werte für Ladepunkt/Verbraucher "+load.num)
else:
data.data.counter_all_data.get_evu_counter().reset_pv_data()
except Exception:
log.exception("Fehler beim Bereinigen der Werte")


def get_load_str(load: Load) -> str:
if isinstance(load, Chargepoint):
return f"Ladepunkt {load.num}"
else:
return f"Verbraucher {load.num}"
10 changes: 10 additions & 0 deletions packages/control/load_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from typing import List, Optional, Protocol

from control.chargepoint.control_parameter import ControlParameter
from helpermodules.timecheck import create_timestamp


@dataclass
Expand Down Expand Up @@ -47,6 +48,15 @@ class Load(Protocol):
submode_changed: bool
data: LoadData

def set_timestamp_charge_start(self) -> None:
# Beim Ladestart Timer laufen lassen, manche Fahrzeuge brauchen sehr lange.
# Nach dem Algorithmus setzen, sonst steht set current noch nicht fest.
if self.data.control_parameter.timestamp_charge_start is None:
if self.data.set.current_prev == 0 and self.data.set.current != 0:
self.data.control_parameter.timestamp_charge_start = create_timestamp()
elif self.data.set.current == 0:
self.data.control_parameter.timestamp_charge_start = None

def is_charging_stop_allowed(self) -> bool:
...

Expand Down
4 changes: 2 additions & 2 deletions packages/control/prepare.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import logging

from control import data
from control import data, load
from modules.common.component_type import ComponentType


Expand Down Expand Up @@ -38,7 +38,7 @@ def setup_algorithm(self) -> None:
data.data.bat_all_data.setup_bat()
data.data.consumer_all_data.get_consumer_sum()
data.data.cp_all_data.get_cp_sum()
data.data.cp_all_data.no_charge()
load.no_charge()
data.data.counter_all_data.set_home_consumption()
except Exception:
log.exception("Fehler im Prepare-Modul")
Expand Down
21 changes: 15 additions & 6 deletions packages/control/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,15 @@ def process_algorithm_results(self) -> None:
try:
self._update_state_consumer(consumer)
if consumer.data.get.state_str is None:
if consumer.data.get.charge_state:
consumer.data.get.state_str = "Verbraucher läuft."
if consumer.data.usage.type == ConsumerUsage.METER_ONLY:
consumer.data.get.state_str = "Messwerte des Verbrauchers werden erfasst."
elif consumer.data.usage.type == ConsumerUsage.SELF_CONTROLLED:
consumer.data.get.state_str = "Messwerte werden an den Verbraucher übermittelt."
else:
consumer.data.get.state_str = "Verbraucher wird gestartet... "
if consumer.data.get.charge_state:
consumer.data.get.state_str = "Verbraucher läuft."
else:
consumer.data.get.state_str = "Verbraucher wird gestartet... "

modules_threads.append(self._start_consumer(consumer))
except Exception:
Expand Down Expand Up @@ -168,17 +173,21 @@ def _start_charging(self, chargepoint: chargepoint.Chargepoint) -> Thread:
name=f"set current cp{chargepoint.chargepoint_module.config.id}")

def _update_state_consumer(self, consumer: Consumer) -> None:
control_parameter = consumer.data.control_parameter

if consumer.data.set.switch_interval_elapsed is False:
log.debug("Intervall für neuen Schaltbefehl nicht abgelaufen.")
consumer.data.set.current = consumer.data.set.current_prev
else:
consumer.data.set.current = round(consumer.data.set.current, 2)
if consumer.data.set.current != 0 and consumer.data.control_parameter.state not in CHARGING_STATES:
consumer.data.control_parameter.state = ChargepointState.CHARGING_ALLOWED
if consumer.data.set.current != 0 and control_parameter.state not in CHARGING_STATES:
control_parameter.state = ChargepointState.CHARGING_ALLOWED
if control_parameter.state != ChargepointState.NO_CHARGING_ALLOWED or consumer.data.set.current != 0:
consumer.set_timestamp_charge_start()
consumer.data.set.power = consumer.data.set.current * \
voltages_mean(consumer.data.get.voltages) * consumer.data.config.connected_phases
log.info(f"Verbraucher{consumer.num}: set current {consumer.data.set.current} A, "
f"state {ChargepointState(consumer.data.control_parameter.state).name}")
f"state {ChargepointState(control_parameter.state).name}")

def _start_consumer(self, consumer: Consumer) -> Thread:
if consumer.data.set.current != consumer.data.set.current_prev:
Expand Down