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
113 changes: 43 additions & 70 deletions packages/helpermodules/measurement_logging/process_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,12 +315,8 @@ def _analyse_energy_source(data, calc_cp: Optional[str] = None) -> Dict:
data["message"] = ""
for i in range(0, len(data["entries"])):
data["entries"][i], message_analyse = analyse_percentage(data["entries"][i])
if calc_cp is not None:
data["entries"][i], message_calc = calc_energy_imported_by_source_cp(
data["entries"][i], calc_cp, data["names"][calc_cp])
else:
data["entries"][i], message_calc = calc_energy_imported_by_source_all(
data["entries"][i], data["names"])
data["entries"][i], message_calc = calc_energy_imported_by_source(
data["entries"][i], data["names"], message_key_filter=calc_cp)
data["message"] += message_analyse + message_calc
data["totals"] = analyse_percentage_totals(data["entries"], data["totals"])
except Exception:
Expand Down Expand Up @@ -405,76 +401,53 @@ def get_grid_counter(entry) -> Dict:
"Fehlerzustand befindet. Die Verbräuche werden mit 0 kWh angesetzt.\n")


def calc_energy_imported_by_source_all(entry, names) -> Tuple[Dict, str]:
def calc_energy_imported_by_source(entry, names, message_key_filter: Optional[str] = None) -> Tuple[Dict, str]:
try:
if "energy_source" not in entry.keys():
return entry, ""
energy_source = entry["energy_source"]
message = ""

hc_section = entry.get("hc")
if isinstance(hc_section, dict) and "all" in hc_section:
hc_all = hc_section["all"]
if isinstance(hc_all, dict):
if hc_all.get("fault_state", 0) != 2 and "energy_imported" in hc_all:
for source in ("grid", "pv", "bat", "cp"):
hc_all[f"energy_imported_{source}"] = decimal_multiply(
hc_all["energy_imported"], energy_source[source])
else:
for source in ("grid", "pv", "bat", "cp"):
hc_all[f"energy_imported_{source}"] = 0
message += ERROR_STATE_MESSAGE.format("den Hausverbrauch")

cp_section = entry.get("cp")
if isinstance(cp_section, dict):
for cp_key, cp_data in cp_section.items():
if isinstance(cp_data, dict):
if cp_data.get("fault_state", 0) != 2 and "energy_imported" in cp_data:
if "energy_source" in entry.keys():
energy_source = entry["energy_source"]
hc_section = entry.get("hc")
if isinstance(hc_section, dict) and "all" in hc_section:
hc_all = hc_section["all"]
if isinstance(hc_all, dict):
if hc_all.get("fault_state", 0) != 2 and "energy_imported" in hc_all:
for source in ("grid", "pv", "bat", "cp"):
cp_data[f"energy_imported_{source}"] = decimal_multiply(
cp_data["energy_imported"], energy_source[source])
hc_all[f"energy_imported_{source}"] = decimal_multiply(
hc_all["energy_imported"], energy_source[source])
else:
for source in ("grid", "pv", "bat", "cp"):
cp_data[f"energy_imported_{source}"] = 0
message += ERROR_STATE_MESSAGE.format(f"Ladepunkt {names.get(cp_key, cp_key)}")

counter_section = entry.get("counter")
if isinstance(counter_section, dict):
for counter_key, counter_data in counter_section.items():
if isinstance(counter_data, dict) and counter_data.get("grid") is False:
if counter_data.get("fault_state", 0) != 2 and "energy_imported" in counter_data:
for source in ("grid", "pv", "bat", "cp"):
counter_data[f"energy_imported_{source}"] = decimal_multiply(
counter_data["energy_imported"], energy_source[source])
else:
for source in ("grid", "pv", "bat", "cp"):
counter_data[f"energy_imported_{source}"] = 0
message += ERROR_STATE_MESSAGE.format(f"Zähler {names[counter_key]}")
except Exception:
log.exception(f"Fehler beim Berechnen der Energie-Anteile aus dem Strom-Mix von {entry['timestamp']}")
message += f"Fehler beim Berechnen des Strom-Mix von {entry['timestamp']}.\n"
finally:
return entry, message


def calc_energy_imported_by_source_cp(entry, cp: str, name: str) -> Tuple[Dict, str]:
try:
if "energy_source" not in entry.keys():
return entry, ""

energy_source = entry["energy_source"]
message = ""

cp_data = entry["cp"][cp]
if cp_data.get("fault_state", 0) != 2 and "energy_imported" in cp_data:
for source in ("grid", "pv", "bat", "cp"):
cp_data[f"energy_imported_{source}"] = decimal_multiply(
cp_data["energy_imported"], energy_source[source])
else:
for source in ("grid", "pv", "bat", "cp"):
cp_data[f"energy_imported_{source}"] = 0
message += ERROR_STATE_MESSAGE.format(f"Ladepunkt {name}")

hc_all[f"energy_imported_{source}"] = 0
if message_key_filter is None or message_key_filter == "hc":
message += ERROR_STATE_MESSAGE.format("den Hausverbrauch")

cp_section = entry.get("cp")
if isinstance(cp_section, dict):
for cp_key, cp_data in cp_section.items():
if isinstance(cp_data, dict):
if cp_data.get("fault_state", 0) != 2 and "energy_imported" in cp_data:
for source in ("grid", "pv", "bat", "cp"):
cp_data[f"energy_imported_{source}"] = decimal_multiply(
cp_data["energy_imported"], energy_source[source])
else:
for source in ("grid", "pv", "bat", "cp"):
cp_data[f"energy_imported_{source}"] = 0
if message_key_filter is None or message_key_filter == cp_key:
message += ERROR_STATE_MESSAGE.format(f"Ladepunkt {names.get(cp_key, cp_key)}")

counter_section = entry.get("counter")
if isinstance(counter_section, dict):
for counter_key, counter_data in counter_section.items():
if isinstance(counter_data, dict) and counter_data.get("grid") is False:
if counter_data.get("fault_state", 0) != 2 and "energy_imported" in counter_data:
for source in ("grid", "pv", "bat", "cp"):
counter_data[f"energy_imported_{source}"] = decimal_multiply(
counter_data["energy_imported"], energy_source[source])
else:
for source in ("grid", "pv", "bat", "cp"):
counter_data[f"energy_imported_{source}"] = 0
if message_key_filter is None or message_key_filter == counter_key:
message += ERROR_STATE_MESSAGE.format(f"Zähler {names.get(counter_key, counter_key)}")
except Exception:
log.exception(f"Fehler beim Berechnen der Energie-Anteile aus dem Strom-Mix von {entry['timestamp']}")
message += f"Fehler beim Berechnen des Strom-Mix von {entry['timestamp']}.\n"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
process_entry,
get_totals,
_collect_daily_log_data,
calc_energy_imported_by_source_all,
calc_energy_imported_by_source,
analyse_percentage_totals,
CalculationType)

Expand Down Expand Up @@ -114,7 +114,7 @@ def test_calculate_average_power():
assert power == 1800


def test_calc_energy_imported_by_source_all():
def test_calc_energy_imported_by_source():
# setup
entry = {
"timestamp": 1234567890,
Expand All @@ -131,7 +131,7 @@ def test_calc_energy_imported_by_source_all():
}

# execution
result, message = calc_energy_imported_by_source_all(entry, {})
result, message = calc_energy_imported_by_source(entry, {})

Comment thread
benderl marked this conversation as resolved.
# evaluation - realistic Wh values with decimal precision
assert result["hc"]["all"]["energy_imported_grid"] == 1530.035
Expand Down Expand Up @@ -222,6 +222,54 @@ def test_analyse_percentage_totals():
assert result["counter"]["counter2"]["energy_imported_cp"] == 824


def test_calc_energy_imported_by_source_message_filtering():
"""Test message filtering when component is in fault state and name is missing."""
# setup
entry = {
"timestamp": 1234567890,
"energy_source": {"grid": 0.6523, "pv": 0.2487, "bat": 0.0789, "cp": 0.0201},
"cp": {
"cp1": {"energy_imported": 15723.4, "fault_state": 0},
"cp2": {"energy_imported": 22108.7, "fault_state": 2} # fault state
},
"counter": {
"counter0": {"grid": True, "energy_imported": 45892.3, "fault_state": 0},
"counter1": {"grid": False, "energy_imported": 8956.7, "fault_state": 2} # fault state
}
}

# Names dict is missing keys for cp2 and counter1
names = {
"cp1": "Ladepunkt 1",
"counter0": "EVU-Zähler"
# cp2 and counter1 intentionally missing
}

# execution - filter messages only for cp2
result, message = calc_energy_imported_by_source(entry, names, message_key_filter="cp2")

# evaluation
# Should only get message for cp2, not counter1 (due to filtering)
expected_message = ("Die Anteile der Energiequellen für Ladepunkt cp2 konnten nicht berechnet werden, da er sich "
"im Fehlerzustand befindet. Die Verbräuche werden mit 0 kWh angesetzt.\n")
assert message == expected_message

# cp2 should have zero values for all energy sources due to fault state
assert result["cp"]["cp2"]["energy_imported_grid"] == 0
assert result["cp"]["cp2"]["energy_imported_pv"] == 0
assert result["cp"]["cp2"]["energy_imported_bat"] == 0
assert result["cp"]["cp2"]["energy_imported_cp"] == 0

# cp1 should have normal calculated values (not in fault state)
assert result["cp"]["cp1"]["energy_imported_grid"] == 10256.374

# counter1 should have zero values but no message (filtered out)
assert result["counter"]["counter1"]["energy_imported_grid"] == 0
assert result["counter"]["counter1"]["energy_imported_pv"] == 0
assert result["counter"]["counter1"]["energy_imported_bat"] == 0
assert result["counter"]["counter1"]["energy_imported_cp"] == 0


def test_convert(daily_log_entry_processed, daily_log_sample):
# setup and execution
entry = process_entry(daily_log_sample[0], daily_log_sample[1], CalculationType.ALL)
Expand Down
Loading