# ============================= # app/simulation/ui/cooling.py # ============================= from __future__ import annotations import tkinter as tk from tkinter import ttk from app.simulation.modules.cooling import COOLING_DEFAULTS from app.simulation.ui import UITab class CoolingTab(UITab): NAME = "cooling" TITLE = "Kühlung" PRIO = 11 def __init__(self, parent, sim): self.sim = sim self.frame = ttk.Frame(parent, padding=8) for c in (0,1,2,3): self.frame.columnconfigure(c, weight=1) # ---------- Linke Spalte ---------- rowL = 0 def L(lbl, var, w=12, kind="entry", values=None): nonlocal rowL ttk.Label(self.frame, text=lbl).grid(row=rowL, column=0, sticky="w") if kind == "entry": ttk.Entry(self.frame, textvariable=var, width=w).grid(row=rowL, column=1, sticky="w") elif kind == "combo": cb = ttk.Combobox(self.frame, textvariable=var, state="readonly", values=values or []) cb.grid(row=rowL, column=1, sticky="w") elif kind == "check": ttk.Checkbutton(self.frame, variable=var).grid(row=rowL, column=1, sticky="w") rowL += 1 self.t_open = tk.DoubleVar(); L("Thermostat öffnet ab [°C]", self.t_open) self.t_full = tk.DoubleVar(); L("Thermostat voll offen [°C]", self.t_full) self.rad_base = tk.DoubleVar(); L("Radiator-Basis [W/K]", self.rad_base) self.ram_gain = tk.DoubleVar(); L("Fahrtwind-Zuwachs [W/K pro km/h]", self.ram_gain) self.amb_c = tk.DoubleVar(); L("Umgebung [°C]", self.amb_c) self.Cc = tk.DoubleVar(); L("Wärmekapazität Kühlmittel [J/K]", self.Cc) self.Coil = tk.DoubleVar(); L("Wärmekapazität Öl [J/K]", self.Coil) ttk.Separator(self.frame).grid(row=rowL, column=0, columnspan=2, sticky="ew", pady=(8,6)); rowL += 1 # Versorgung & Nachlauf (links) self.feed = tk.StringVar() self.afteren = tk.BooleanVar() self.afterth = tk.DoubleVar() self.aftermax= tk.DoubleVar() L("Lüfter-Versorgung", self.feed, kind="combo", values=["elx", "battery"]) L("Nachlauf aktiv", self.afteren, kind="check") L("Nachlauf-Schwelle [°C]", self.afterth) L("Nachlauf max. Zeit [s]", self.aftermax) # ---------- Rechte Spalte ---------- rowR = 0 def R(lbl, var, w=12): nonlocal rowR ttk.Label(self.frame, text=lbl).grid(row=rowR, column=2, sticky="w") ttk.Entry(self.frame, textvariable=var, width=w).grid(row=rowR, column=3, sticky="w") rowR += 1 self.f1_on = tk.DoubleVar(); R("Lüfter 1 EIN [°C]", self.f1_on) self.f1_off = tk.DoubleVar(); R("Lüfter 1 AUS [°C]", self.f1_off) self.f2_on = tk.DoubleVar(); R("Lüfter 2 EIN [°C]", self.f2_on) self.f2_off = tk.DoubleVar(); R("Lüfter 2 AUS [°C]", self.f2_off) ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1 self.f1_w = tk.DoubleVar(); R("Lüfter 1 Leistung [W]", self.f1_w) self.f2_w = tk.DoubleVar(); R("Lüfter 2 Leistung [W]", self.f2_w) self.f1_air = tk.DoubleVar(); R("Lüfter 1 Luftstrom [W/K]", self.f1_air) self.f2_air = tk.DoubleVar(); R("Lüfter 2 Luftstrom [W/K]", self.f2_air) ttk.Separator(self.frame).grid(row=rowR, column=2, columnspan=2, sticky="ew", pady=(8,6)); rowR += 1 self.Uoc = tk.DoubleVar(); R("Öl↔Kühlmittel Kopplung [W/K]", self.Uoc) self.Uoil = tk.DoubleVar(); R("Öl→Umgebung [W/K]", self.Uoil) self.frac = tk.DoubleVar(); R("Motorwärme→Kühlmittel [%]", self.frac) # ---------- Buttons ---------- rowBtns = max(rowL, rowR) + 1 btnrow = ttk.Frame(self.frame) btnrow.grid(row=rowBtns, column=0, columnspan=4, sticky="w", pady=(8,0)) ttk.Button(btnrow, text="Aktualisieren", command=self.refresh).pack(side="left") ttk.Button(btnrow, text="Anwenden", command=self.apply).pack(side="left", padx=(8,0)) self.refresh() def refresh(self): c = dict(COOLING_DEFAULTS) c.update(self.sim.v.config.get("cooling", {})) # links self.t_open.set(c["thermostat_open_c"]) self.t_full.set(c["thermostat_full_c"]) self.rad_base.set(c["rad_base_u_w_per_k"]) self.ram_gain.set(c["ram_air_gain_per_kmh"]) self.amb_c.set(self.sim.v.get("ambient_c", 20.0)) self.Cc.set(c["coolant_thermal_cap_j_per_k"]) self.Coil.set(c["oil_thermal_cap_j_per_k"]) # Versorgung & Nachlauf self.feed.set(c.get("fan_power_feed", "elx")) self.afteren.set(bool(c.get("fan_afterrun_enable", False))) self.afterth.set(float(c.get("fan_afterrun_threshold_c", 105.0))) self.aftermax.set(float(c.get("fan_afterrun_max_s", 300.0))) # rechts self.f1_on.set(c["fan1_on_c"]); self.f1_off.set(c["fan1_off_c"]) self.f2_on.set(c["fan2_on_c"]); self.f2_off.set(c["fan2_off_c"]) self.f1_w.set(c["fan1_power_w"]); self.f2_w.set(c["fan2_power_w"]) self.f1_air.set(c["fan1_airflow_gain"]); self.f2_air.set(c["fan2_airflow_gain"]) self.Uoc.set(c["oil_coolant_u_w_per_k"]) self.Uoil.set(c["oil_to_amb_u_w_per_k"]) self.frac.set(c["engine_heat_frac_to_coolant"]*100.0) def apply(self): cfg = {"cooling": { # links "thermostat_open_c": float(self.t_open.get()), "thermostat_full_c": float(self.t_full.get()), "rad_base_u_w_per_k": float(self.rad_base.get()), "ram_air_gain_per_kmh": float(self.ram_gain.get()), "coolant_thermal_cap_j_per_k": float(self.Cc.get()), "oil_thermal_cap_j_per_k": float(self.Coil.get()), # Versorgung & Nachlauf "fan_power_feed": self.feed.get(), "fan_afterrun_enable": bool(self.afteren.get()), "fan_afterrun_threshold_c": float(self.afterth.get()), "fan_afterrun_max_s": float(self.aftermax.get()), # rechts "fan1_on_c": float(self.f1_on.get()), "fan1_off_c": float(self.f1_off.get()), "fan2_on_c": float(self.f2_on.get()), "fan2_off_c": float(self.f2_off.get()), "fan1_power_w": float(self.f1_w.get()), "fan2_power_w": float(self.f2_w.get()), "fan1_airflow_gain": float(self.f1_air.get()), "fan2_airflow_gain": float(self.f2_air.get()), "oil_coolant_u_w_per_k": float(self.Uoc.get()), "oil_to_amb_u_w_per_k": float(self.Uoil.get()), "engine_heat_frac_to_coolant": float(self.frac.get())/100.0, }} self.sim.load_config(cfg)