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
22 changes: 18 additions & 4 deletions packages/control/algorithm/filter_chargepoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,20 @@ def _is_valid_for_chargemode(entity: Load,
return _group_loads_by_chargemode(chargemodes, _is_valid_for_chargemode)[1]


def _get_consumer_by_prio_item(item: dict) -> Optional[Consumer]:
"""Liefert den Verbraucher zu einem Eintrag der Prioritätensteuerung.

Ein Eintrag kann kurzzeitig auf einen bereits gelöschten Verbraucher verweisen, wenn dessen Entfernung
aus der Prioritätensteuerung fehlgeschlagen ist. Ein einzelner solcher Karteileichen-Eintrag darf nicht
die komplette Regelung für alle Ladepunkte zum Absturz bringen.
"""
consumer = data.data.consumer_data.get(f"{item['type']}{item['id']}")
if consumer is None:
log.warning(f"Verbraucher {item['id']} aus der Prioritätensteuerung existiert nicht (mehr), "
"wird ignoriert.")
return consumer


def _group_loads_by_chargemode(chargemodes: Tuple[Tuple[Optional[str], str]],
filter_func) -> Tuple[List[Load], List[List[Load]]]:
grouped_loads: List[List[Load]] = []
Expand All @@ -48,8 +62,8 @@ def _group_loads_by_chargemode(chargemodes: Tuple[Tuple[Optional[str], str]],
sub_valid_chargemode.append(cp)
flat_loads.append(cp)
elif group_item["type"] == "consumer":
consumer = data.data.consumer_data[f"{group_item['type']}{group_item['id']}"]
if filter_func(consumer, chargemode, flat_loads):
consumer = _get_consumer_by_prio_item(group_item)
if consumer is not None and filter_func(consumer, chargemode, flat_loads):
sub_valid_chargemode.append(consumer)
flat_loads.append(consumer)
grouped_loads.append(sub_valid_chargemode)
Expand All @@ -60,8 +74,8 @@ def _group_loads_by_chargemode(chargemodes: Tuple[Tuple[Optional[str], str]],
grouped_loads.append([cp])
flat_loads.append(cp)
elif item["type"] == "consumer":
consumer = data.data.consumer_data[f"{item['type']}{item['id']}"]
if filter_func(consumer, chargemode, flat_loads):
consumer = _get_consumer_by_prio_item(item)
if consumer is not None and filter_func(consumer, chargemode, flat_loads):
grouped_loads.append([consumer])
flat_loads.append(consumer)
return flat_loads, grouped_loads
Expand Down
19 changes: 19 additions & 0 deletions packages/control/algorithm/filter_chargepoints_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ def mock_consumer3() -> Consumer:
{"type": "consumer", "id": 3},
{"type": "vehicle", "id": 2}],
[1, 3, 2], id="required current 0 for cp 1"),
pytest.param(6, 1, ((Chargemode.SCHEDULED_CHARGING, Chargemode.INSTANT_CHARGING),),
[{"type": "vehicle", "id": 1},
# Verbraucher 99 wurde gelöscht, ist aber (Bug) noch in der Prioritätensteuerung
# gelistet. Darf die Ermittlung der anderen Lasten nicht zum Absturz bringen.
{"type": "consumer", "id": 99},
{"type": "vehicle", "id": 2},
{"type": "consumer", "id": 3}],
[1, 3], id="stale consumer reference is skipped, not fatal"),
pytest.param(6, 1, ((Chargemode.SCHEDULED_CHARGING, Chargemode.INSTANT_CHARGING),),
[{"type": "vehicle", "id": 1},
{
"type": "group",
"label": "Gruppe 1",
"children": [
{"type": "vehicle", "id": 2},
# ebenfalls gelöschter, aber noch gelisteter Verbraucher, diesmal in einer Gruppe
{"type": "consumer", "id": 99},
{"type": "consumer", "id": 3}]}],
[1, 3], id="stale consumer reference in group is skipped, not fatal"),
])
def test_get_loads_by_chargemodes(
required_current_1: int,
Expand Down
4 changes: 3 additions & 1 deletion packages/control/counter_all/loadmanagement_prio.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ def add_loadmanagement_prio_item(self: LoadmanagementPrioProtocol, type: Compone

def remove_loadmanagement_prio_item(self: LoadmanagementPrioProtocol, type: ComponentType, id: int) -> None:
if self._remove_loadmanagement_prio_item(type, id, self.data.get.loadmanagement_prios) is False:
raise IndexError(f"Element {id} konnte nicht in der Prioritätensteuerung gefunden werden.")
# Kein Grund, die Löschung des restlichen Elements (Topics, Hierarchie) abzubrechen, wenn der
# Eintrag in der Prioritätensteuerung schon fehlt.
log.warning(f"Element {type.value}/{id} konnte nicht in der Prioritätensteuerung gefunden werden.")

def _remove_loadmanagement_prio_item(self: LoadmanagementPrioProtocol,
type: ComponentType,
Expand Down
3 changes: 3 additions & 0 deletions packages/control/counter_all/loadmanagement_prio_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ def test_add_item(loadmanagement_prios: List[Dict],
ComponentType.VEHICLE, 2, [{"type": "vehicle", "id": 3}], id="flat list, remove vehicle"),
pytest.param([{"type": "vehicle", "id": 3}, {"type": "consumer", "id": 2}],
ComponentType.CONSUMER, 2, [{"type": "vehicle", "id": 3}], id="flat list, remove consumer"),
pytest.param([{"type": "vehicle", "id": 3}, {"type": "consumer", "id": 2}],
ComponentType.CONSUMER, 99, [{"type": "vehicle", "id": 3}, {"type": "consumer", "id": 2}],
id="id not present, must not raise and must not change the list"),
pytest.param([
{
"type": "group",
Expand Down
1 change: 1 addition & 0 deletions packages/helpermodules/setdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -1248,6 +1248,7 @@ def process_consumer_topic(self, msg):
"openWB/set/consumer/get/daily_exported" in msg.topic):
self._validate_value(msg, float)
elif (re.search("openWB/set/consumer/[0-9]+/module$", msg.topic) is not None or
re.search("openWB/set/consumer/[0-9]+/module/simulation$", msg.topic) is not None or
re.search("openWB/set/consumer/[0-9]+/config$", msg.topic) is not None or
re.search("openWB/set/consumer/[0-9]+/usage$", msg.topic) is not None):
self._validate_value(msg, "json")
Expand Down
25 changes: 18 additions & 7 deletions packages/helpermodules/subdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -1248,14 +1248,25 @@ def process_consumer_topic(self, client: mqtt.Client, var: Dict[str, Consumer],
else:
if f"consumer{index}" not in var:
var[f"consumer{index}"] = Consumer(int(index))
if re.search("openWB/consumer/[0-9]+/module", msg.topic) is not None:
if re.search("openWB/consumer/[0-9]+/module$", msg.topic) is not None:
consumer_config = decode_payload(msg.payload)
con = importlib.import_module(
f".consumers.{consumer_config['vendor']}.{consumer_config['type']}.consumer",
"modules")
config = dataclass_from_dict(con.device_descriptor.configuration_factory, consumer_config)
var["consumer"+index].module = con.create_consumer(config)
var["consumer"+index].data.module = config
try:
con = importlib.import_module(
f".consumers.{consumer_config['vendor']}.{consumer_config['type']}.consumer",
"modules")
config = dataclass_from_dict(
con.device_descriptor.configuration_factory, consumer_config)
var["consumer"+index].module = con.create_consumer(config)
var["consumer"+index].data.module = config
except Exception:
fault_str = (
f"Verbraucher {index}: Modul für "
f"{consumer_config.get('vendor')}/{consumer_config.get('type')} konnte nicht "
"erstellt werden, siehe Log.")
log.exception(fault_str)
var["consumer"+index].data.get.fault_state = 2
var["consumer"+index].data.get.fault_str = fault_str
pub_system_message({}, fault_str, MessageType.ERROR)
elif re.search("openWB/consumer/[0-9]+/config", msg.topic) is not None:
self.set_json_payload_class(var["consumer"+index].data.config, msg)
elif re.search("openWB/consumer/[0-9]+/get", msg.topic) is not None:
Expand Down
6 changes: 6 additions & 0 deletions packages/modules/loadvars.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ def _set_values(self) -> List[str]:
log.exception(f"Fehler im loadvars-Modul bei Element {cp.num}")
for consumer in data.data.consumer_data.values():
try:
if consumer.module is None:
# Verbraucher, dessen Modul nicht erstellt werden konnte (z.B. defekte Konfiguration).
continue
modules_threads.append(Thread(target=consumer.module.update,
args=(),
name=f"set values consumer{consumer.data.module.id}"))
Expand Down Expand Up @@ -113,6 +116,9 @@ def _update_values_of_level_buttom_top(self, elements, not_finished_threads: Lis
name=f"update values cp{chargepoint.chargepoint_module.config.id}"))
elif element["type"] == ComponentType.CONSUMER.value:
consumer = data.data.consumer_data[f'{type_to_topic_mapping(element["type"])}{element["id"]}']
if consumer.module is None:
# Verbraucher, dessen Modul nicht erstellt werden konnte (z.B. defekte Konfiguration).
continue
thread_name = f"set values consumer{consumer.data.module.id}"
if thread_name not in not_finished_threads:
modules_threads.append(Thread(
Expand Down