Skip to content

Commit 95c8c31

Browse files
authored
Ticket (#1726)
* draft * ticket * remove comment * delete old script * fixes * secondary * fix * show primary/secondary mode * fix
1 parent fecf9ba commit 95c8c31

File tree

4 files changed

+276
-154
lines changed

4 files changed

+276
-154
lines changed

packages/helpermodules/command.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from helpermodules.data_migration.data_migration import MigrateData
2424
from helpermodules.measurement_logging.process_log import get_daily_log, get_monthly_log, get_yearly_log
2525
from helpermodules.messaging import MessageType, pub_user_message
26-
from helpermodules.parse_send_debug import parse_send_debug_data
26+
from helpermodules.create_debug import create_debug_log
2727
from helpermodules.pub import Pub, pub_single
2828
from helpermodules.subdata import SubData
2929
from helpermodules.utils.topic_parser import decode_payload
@@ -536,10 +536,8 @@ def removeVehicle(self, connection_id: str, payload: dict) -> None:
536536

537537
def sendDebug(self, connection_id: str, payload: dict) -> None:
538538
pub_user_message(payload, connection_id, "Systembericht wird erstellt...", MessageType.INFO)
539-
parent_file = Path(__file__).resolve().parents[2]
540539
previous_log_level = SubData.system_data["system"].data["debug_level"]
541-
run_command([str(parent_file / "runs" / "send_debug.sh"),
542-
json.dumps(payload["data"]), parse_send_debug_data()])
540+
create_debug_log(payload["data"])
543541
Pub().pub("openWB/set/system/debug_level", previous_log_level)
544542
pub_user_message(payload, connection_id, "Systembericht wurde versandt.", MessageType.SUCCESS)
545543

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
import os
2+
import time
3+
import logging
4+
from pathlib import Path
5+
import pprint
6+
from typing import Any, Optional
7+
from control import data
8+
from control.chargepoint.chargepoint import Chargepoint
9+
import dataclass_utils
10+
from helpermodules import subdata
11+
from helpermodules.broker import InternalBrokerClient
12+
from helpermodules.pub import Pub
13+
from helpermodules.utils.run_command import run_command
14+
from helpermodules.utils.topic_parser import decode_payload
15+
from modules.common import req
16+
from modules.common.abstract_device import AbstractDevice
17+
18+
log = logging.getLogger(__name__)
19+
20+
21+
def config_and_state():
22+
parsed_data = ""
23+
try:
24+
secondary = subdata.SubData.general_data.data.extern
25+
except Exception:
26+
secondary = False
27+
28+
with ErrorHandlingContext():
29+
parent_file = Path(__file__).resolve().parents[2]
30+
with open(f"{parent_file}/web/version", "r") as f:
31+
version = f.read().strip()
32+
with open(f"{parent_file}/web/lastcommit", "r") as f:
33+
lastcommit = f.read().strip()
34+
parsed_data += f"# Version\n{version}\n{lastcommit}\n\n"
35+
with ErrorHandlingContext():
36+
parsed_data += f"# Cloud/Brücken\n{BrokerContent().get_bridges()}"
37+
with ErrorHandlingContext():
38+
chargemode_config = data.data.general_data.data.chargemode_config
39+
parsed_data += "\n# Allgemein\n"
40+
if secondary is False:
41+
parsed_data += (f"Modus: Primary\nHausverbrauch: {data.data.counter_all_data.data.set.home_consumption}W\n"
42+
f"Phasenvorgabe: Sofortladen {chargemode_config.instant_charging.phases_to_use}, Zielladen "
43+
f"{chargemode_config.scheduled_charging.phases_to_use}, Zeitladen: "
44+
f"{chargemode_config.time_charging.phases_to_use}, PV-Laden: "
45+
f"{chargemode_config.pv_charging.phases_to_use}, Einschaltschwelle: "
46+
f"{chargemode_config.pv_charging.switch_on_threshold}W, Ausschaltschwelle: "
47+
f"{chargemode_config.pv_charging.switch_off_threshold}W\n"
48+
f"Regelintervall: {data.data.general_data.data.control_interval}s, ")
49+
else:
50+
parsed_data += "Modus: Secondary\n"
51+
parsed_data += f"Display aktiviert: {data.data.optional_data.data.int_display.active}\n"
52+
53+
if secondary is False:
54+
with ErrorHandlingContext():
55+
pretty_hierarchy = pprint.pformat(data.data.counter_all_data.data.get.hierarchy,
56+
indent=4, compact=True, sort_dicts=False, width=100)
57+
parsed_data += f"\n# Hierarchie\n{pretty_hierarchy}\n"
58+
59+
with ErrorHandlingContext():
60+
if secondary:
61+
with ErrorHandlingContext():
62+
parsed_data += "\n# Ladepunkte\n"
63+
for cp in subdata.SubData.cp_data.values():
64+
parsed_data += get_parsed_cp_data(cp.chargepoint)
65+
else:
66+
parsed_data += "\n# Geräte und Komponenten\n"
67+
for key, value in data.data.system_data.items():
68+
with ErrorHandlingContext():
69+
if isinstance(value, AbstractDevice):
70+
parsed_data += f"{key}: {dataclass_utils.asdict(value.device_config)}\n"
71+
for comp_key, comp_value in value.components.items():
72+
parsed_data += f"{comp_key}: {dataclass_utils.asdict(comp_value.component_config)}\n"
73+
if "bat" in comp_value.component_config.type:
74+
component_data = data.data.bat_data[f"bat{comp_value.component_config.id}"]
75+
elif "counter" in comp_value.component_config.type:
76+
component_data = data.data.counter_data[f"counter{comp_value.component_config.id}"]
77+
elif "inverter" in comp_value.component_config.type:
78+
component_data = data.data.pv_data[f"pv{comp_value.component_config.id}"]
79+
if "bat" in comp_value.component_config.type:
80+
parsed_data += (f"Leistung: {component_data.data.get.power/1000}kW, "
81+
f"SoC: {component_data.data.get.soc}%, "
82+
f"Fehlerstatus: {component_data.data.get.fault_str}\n")
83+
elif "inverter" in comp_value.component_config.type:
84+
parsed_data += (f"Leistung: {component_data.data.get.power/1000}kW, "
85+
f"Fehlerstatus: {component_data.data.get.fault_str}\n")
86+
else:
87+
counter_all_data = data.data.counter_all_data
88+
if counter_all_data.get_evu_counter_str() == f"counter{component_data.num}":
89+
parsed_data += (f"{comp_key}: EVU-Zähler -> max. Leistung "
90+
f"{component_data.data.config.max_total_power}, "
91+
f"max. Ströme {component_data.data.config.max_currents}; ")
92+
elif counter_all_data.data.config.home_consumption_source_id == component_data.num:
93+
parsed_data += (f"{comp_key}: Hausverbrauchszähler -> max. Leistung "
94+
f"{component_data.data.config.max_total_power}, "
95+
f"max. Ströme {component_data.data.config.max_currents}; ")
96+
else:
97+
parsed_data += f"{key}: max. Ströme {component_data.data.config.max_currents}"
98+
parsed_data += (f"Leistung: {component_data.data.get.power/1000}kW, Ströme: "
99+
f"{component_data.data.get.currents}A, Fehlerstatus: "
100+
f"{component_data.data.get.fault_str}\n")
101+
with ErrorHandlingContext():
102+
parsed_data += "\n# Ladepunkte\n"
103+
parsed_data += f"Ladeleistung aller Ladepunkte {data.data.cp_all_data.data.get.power / 1000}kW\n"
104+
for cp in data.data.cp_data.values():
105+
parsed_data += get_parsed_cp_data(cp)
106+
return parsed_data
107+
108+
109+
def get_parsed_cp_data(cp: Chargepoint) -> str:
110+
parsed_data = ""
111+
with ErrorHandlingContext():
112+
if hasattr(cp.chargepoint_module.config.configuration, "ip_address"):
113+
ip = cp.chargepoint_module.config.configuration.ip_address
114+
else:
115+
ip = None
116+
parsed_data += (f"LP{cp.num}: Typ: {cp.chargepoint_module.config.type}; IP: "
117+
f"{ip}; Stecker-Status: {cp.data.get.plug_state}, Leistung: "
118+
f"{cp.data.get.power/1000}kW, {cp.data.get.currents}A, {cp.data.get.voltages}V, Lademodus: "
119+
f"{cp.data.control_parameter.chargemode}, Submode: "
120+
f"{cp.data.control_parameter.submode}, Sollstrom: "
121+
f"{cp.data.set.current}A, Status: {cp.data.get.state_str}, "
122+
f"Fehlerstatus: {cp.data.get.fault_str}\n")
123+
if cp.chargepoint_module.config.type == "openwb_pro":
124+
parsed_data += f"{req.get_http_session().get(f'http://{ip}/connect.php').text}\n"
125+
return parsed_data
126+
127+
128+
openwb_base_dir = Path(__file__).resolve().parents[2]
129+
ramdisk_dir = openwb_base_dir / 'ramdisk'
130+
debug_file = ramdisk_dir / 'debug.log'
131+
132+
133+
def merge_log_files(log_name, num_lines):
134+
log_files = [f"{ramdisk_dir}/{log_name}.log.{i}" for i in range(5, 1)]
135+
log_files.append(f"{ramdisk_dir}/{log_name}.log")
136+
137+
lines = []
138+
try:
139+
for log_file in log_files:
140+
if os.path.isfile(log_file):
141+
with open(log_file, 'r') as file:
142+
lines += file.readlines()
143+
except Exception as e:
144+
log.exception(f"Fehler beim Lesen der Logdateien: {e}")
145+
return ''.join(lines[-num_lines:])
146+
147+
148+
def get_uuids():
149+
try:
150+
with open(openwb_base_dir / 'data/log/uuid', 'r') as uuid_file:
151+
return (uuid_file.read())
152+
except Exception as e:
153+
log.exception(f"Error reading UUID file: {e}")
154+
155+
156+
def create_debug_log(input_data):
157+
def write_to_file(file_handler, func, default: Optional[Any] = None):
158+
try:
159+
file_handler.write(func()+"\n")
160+
except Exception as e:
161+
log.exception(f"Error getting value for chargelog: {func}. Setting to default {default}.")
162+
file_handler.write(f"Error getting value for chargelog: {func}. Setting to default {default}.\n"
163+
f"Error: {e}\n")
164+
165+
try:
166+
broker = BrokerContent()
167+
debug_email = input_data.get('email', '')
168+
header = (f"{input_data['message']}\n{debug_email}\n{input_data['serialNumber']}\n"
169+
f"{input_data['installedComponents']}\n{input_data['vehicles']}\n")
170+
with open(debug_file, 'w+') as df:
171+
write_to_file(df, lambda: header)
172+
write_to_file(df, lambda: f"## section: configuration and state ##\n{config_and_state()}\n")
173+
write_to_file(df, lambda: f'## section: system ##\n{run_command(["uptime"])}{run_command(["free"])}\n')
174+
write_to_file(df, lambda: f"## section: uuids ##\n{get_uuids()}\n")
175+
write_to_file(df, lambda: f'## section: network ##\n{run_command(["ifconfig"])}\n')
176+
write_to_file(df, lambda: f'## section: storage ##\n{run_command(["df", "-h"])}\n')
177+
write_to_file(df, lambda: f"## section: broker essentials ##\n{broker.get_broker_essentials()}\n")
178+
write_to_file(
179+
df, lambda: f"## section: retained log ##\n{merge_log_files('main', 500)}")
180+
write_to_file(df, lambda: "## section: info log ##\n")
181+
Pub().pub('openWB/set/system/debug_level', 20)
182+
time.sleep(60)
183+
write_to_file(df, lambda: merge_log_files("main", 1000))
184+
write_to_file(df, lambda: "## section: debug log ##\n")
185+
Pub().pub('openWB/set/system/debug_level', 10)
186+
time.sleep(60)
187+
write_to_file(df, lambda: merge_log_files("main", 2500))
188+
write_to_file(
189+
df,
190+
lambda: f'## section: internal chargepoint log ##\n{merge_log_files("internal_chargepoint", 1000)}\n')
191+
write_to_file(df, lambda: f'## section: mqtt log ##\n{merge_log_files("mqtt", 1000)}\n')
192+
write_to_file(df, lambda: f'## section: soc log ##\n{merge_log_files("soc", 1000)}\n')
193+
write_to_file(df, lambda: f'## section: charge log ##\n{merge_log_files("chargelog", 1000)}\n')
194+
write_to_file(df, lambda: f"## section: broker ##\n{broker.get_broker()}")
195+
196+
log.info("***** uploading debug log...")
197+
with open(debug_file, 'rb') as f:
198+
data = f.read()
199+
req.get_http_session().put("https://openwb.de/tools/debug2.php",
200+
data=data,
201+
params={'debugemail': debug_email})
202+
203+
log.info("***** cleanup...")
204+
os.remove(debug_file)
205+
log.info("***** debug log end")
206+
except Exception as e:
207+
log.exception(f"Error creating debug log: {e}")
208+
209+
210+
class BrokerContent:
211+
def __init__(self) -> None:
212+
self.content = ""
213+
214+
def get_broker(self):
215+
InternalBrokerClient("processBrokerBranch", self.__on_connect_broker, self.__get_content).start_finite_loop()
216+
return self.content
217+
218+
def __on_connect_broker(self, client, userdata, flags, rc):
219+
client.subscribe("openWB/#", 2)
220+
221+
def __get_content(self, client, userdata, msg):
222+
self.content += f"{msg.topic} {decode_payload(msg.payload)}\n"
223+
224+
def get_broker_essentials(self):
225+
InternalBrokerClient("processBrokerBranch", self.__on_connect_broker_essentials,
226+
self.__get_content).start_finite_loop()
227+
return self.content
228+
229+
def __on_connect_broker_essentials(self, client, userdata, flags, rc):
230+
client.subscribe("openWB/system/ip_address", 2)
231+
client.subscribe("openWB/system/current_commit", 2)
232+
client.subscribe("openWB/system/boot_done", 2)
233+
client.subscribe("openWB/system/update_in_progress", 2)
234+
client.subscribe("openWB/system/device/#", 2)
235+
client.subscribe("openWB/system/time", 2)
236+
client.subscribe("openWB/chargepoint/#", 2)
237+
client.subscribe("openWB/internal_chargepoint/#", 2)
238+
client.subscribe("openWB/vehicle/#", 2)
239+
client.subscribe("openWB/counter/#", 2)
240+
client.subscribe("openWB/pv/#", 2)
241+
client.subscribe("openWB/bat/#", 2)
242+
client.subscribe("openWB/optional/et/provider", 2)
243+
244+
def get_bridges(self):
245+
InternalBrokerClient("processBrokerBranch", self.__on_connect_bridges, self.__get_bridges).start_finite_loop()
246+
return self.content
247+
248+
def __on_connect_bridges(self, client, userdata, flags, rc):
249+
client.subscribe("openWB/system/mqtt/#", 2)
250+
251+
def __get_bridges(self, client, userdata, msg):
252+
if "openWB/system/mqtt/bridge" in msg.topic:
253+
payload = decode_payload(msg.payload)
254+
self.content += (f"Name: {payload['name']}, aktiv: {payload['active']}, "
255+
f"openWB-Cloud: {payload['remote']['is_openwb_cloud']}")
256+
if payload.get("is_openwb_cloud"):
257+
self.content += (f", BN: {payload['remote']['username']}, PW: {payload['remote']['password']}, "
258+
f"Partnerzugang: {payload['access']['partner']}")
259+
self.content += "\n"
260+
elif "openWB/system/mqtt/valid_partner_ids":
261+
self.content += f"Partner-IDs: {decode_payload(msg.payload)}"
262+
263+
264+
class ErrorHandlingContext:
265+
def __init__(self):
266+
pass
267+
268+
def __enter__(self):
269+
return None
270+
271+
def __exit__(self, exception_type, exception, exception_traceback) -> bool:
272+
if isinstance(exception, Exception):
273+
log.exception("Fehler beim Parsen der Daten für das Support-Ticket")
274+
return True

0 commit comments

Comments
 (0)