# ============================= # app/simulation/ui/engine.py # ============================= from __future__ import annotations import tkinter as tk from tkinter import ttk from typing import Dict, Any # Wichtig: Defaults aus dem Modul importieren from app.simulation.modules.engine import ENGINE_DEFAULTS from app.simulation.ui import UITab class EngineTab(UITab): NAME = "engine" TITLE = "Motor" PRIO = 10 def __init__(self, parent, sim): self.sim = sim self.frame = ttk.Frame(parent, padding=8) self.frame.columnconfigure(1, weight=1) # ------------- Widgets anlegen (OHNE Defaultwerte eintragen) -------------- row = 0 ttk.Label(self.frame, text="Leerlauf [RPM]").grid(row=row, column=0, sticky="w"); row+=1 self.idle_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.idle_var, width=10)\ .grid(row=row-1, column=1, sticky="w") ttk.Label(self.frame, text="Max RPM").grid(row=row, column=0, sticky="w"); row+=1 self.maxrpm_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.maxrpm_var, width=10)\ .grid(row=row-1, column=1, sticky="w") ttk.Label(self.frame, text="Anstieg [RPM/s]").grid(row=row, column=0, sticky="w"); row+=1 self.rise_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.rise_var, width=10)\ .grid(row=row-1, column=1, sticky="w") ttk.Label(self.frame, text="Abfall [RPM/s]").grid(row=row, column=0, sticky="w"); row+=1 self.fall_var = tk.IntVar(); ttk.Entry(self.frame, textvariable=self.fall_var, width=10)\ .grid(row=row-1, column=1, sticky="w") ttk.Label(self.frame, text="Gaspedal-Kennlinie").grid(row=row, column=0, sticky="w"); row+=1 self.thr_curve = tk.StringVar() ttk.Combobox(self.frame, textvariable=self.thr_curve, state="readonly", values=["linear","progressive","aggressive"])\ .grid(row=row-1, column=1, sticky="w") ttk.Separator(self.frame).grid(row=row, column=0, columnspan=2, sticky="ew", pady=(8,6)); row+=1 # Leistung ttk.Label(self.frame, text="Motorleistung [kW]").grid(row=row, column=0, sticky="w"); row+=1 self.power_kw = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.power_kw, width=10)\ .grid(row=row-1, column=1, sticky="w") ttk.Label(self.frame, text="Drehmoment-Peak [RPM]").grid(row=row, column=0, sticky="w"); row+=1 self.peak_rpm = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.peak_rpm, width=10)\ .grid(row=row-1, column=1, sticky="w") ttk.Separator(self.frame).grid(row=row, column=0, columnspan=2, sticky="ew", pady=(8,6)); row+=1 # Starter ttk.Label(self.frame, text="Starter Nenn-RPM").grid(row=row, column=0, sticky="w"); row+=1 self.starter_nom = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.starter_nom, width=10)\ .grid(row=row-1, column=1, sticky="w") ttk.Label(self.frame, text="Starter min. Spannung [V]").grid(row=row, column=0, sticky="w"); row+=1 self.starter_vmin = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.starter_vmin, width=10)\ .grid(row=row-1, column=1, sticky="w") ttk.Label(self.frame, text="Start-Schwelle [RPM]").grid(row=row, column=0, sticky="w"); row+=1 self.start_th = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.start_th, width=10)\ .grid(row=row-1, column=1, sticky="w") ttk.Label(self.frame, text="Stall-Grenze [RPM]").grid(row=row, column=0, sticky="w"); row+=1 self.stall_rpm = tk.DoubleVar(); ttk.Entry(self.frame, textvariable=self.stall_rpm, width=10)\ .grid(row=row-1, column=1, sticky="w") ttk.Separator(self.frame).grid(row=row, column=0, columnspan=2, sticky="ew", pady=(8,6)); row+=1 # Thermik (analog – Variablen ohne Defaults anlegen) ... self.amb_c = tk.DoubleVar(); self.c_warm = tk.DoubleVar(); self.c_cool = tk.DoubleVar() self.o_warm = tk.DoubleVar(); self.o_cool = tk.DoubleVar() self.cold_gain = tk.DoubleVar(); self.cold_gain_max = tk.DoubleVar() # (Labels/Entries spar ich hier ab – wie gehabt weiterführen) # Öl, DBW, Jitter, Pedal self.o_idle = tk.DoubleVar(); self.o_slope = tk.DoubleVar(); self.o_floor = tk.DoubleVar() self.plate_idle_min = tk.DoubleVar(); self.plate_overrun = tk.DoubleVar(); self.plate_tau = tk.DoubleVar() self.torque_kp = tk.DoubleVar(); self.torque_ki = tk.DoubleVar() self.jitter_idle = tk.DoubleVar(); self.jitter_high = tk.DoubleVar() self.jitter_tau = tk.DoubleVar(); self.jitter_off = tk.DoubleVar() ttk.Label(self.frame, text="Gaspedal [%]").grid(row=row, column=0, sticky="w"); row+=1 self.pedal_var = tk.DoubleVar() self.pedal_scale = ttk.Scale(self.frame, from_=0.0, to=100.0, variable=self.pedal_var) self.pedal_scale.grid(row=row-1, column=1, sticky="ew") # Buttons row += 1 btnrow = ttk.Frame(self.frame); btnrow.grid(row=row, column=0, columnspan=2, 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)) # Zum Start einmal „live“ laden: self.refresh() # liest IMMER effektiv: config.get(key, ENGINE_DEFAULTS[key]) def refresh(self): e = dict(ENGINE_DEFAULTS) e.update(self.sim.v.config.get("engine", {})) # Config über default mergen self.idle_var.set(e["idle_rpm"]) self.maxrpm_var.set(e["max_rpm"]) self.rise_var.set(e["rpm_rise_per_s"]) self.fall_var.set(e["rpm_fall_per_s"]) self.thr_curve.set(e["throttle_curve"]) self.power_kw.set(e["engine_power_kw"]) self.peak_rpm.set(e["torque_peak_rpm"]) self.starter_nom.set(e["starter_rpm_nominal"]) self.starter_vmin.set(e["starter_voltage_min"]) self.start_th.set(e["start_rpm_threshold"]) self.stall_rpm.set(e["stall_rpm"]) self.amb_c.set(e["coolant_ambient_c"]) self.c_warm.set(e["coolant_warm_rate_c_per_s"]) self.c_cool.set(e["coolant_cool_rate_c_per_s"]) self.o_warm.set(e["oil_warm_rate_c_per_s"]) self.o_cool.set(e["oil_cool_rate_c_per_s"]) self.cold_gain.set(e["idle_cold_gain_per_deg"]) self.cold_gain_max.set(e["idle_cold_gain_max"]) self.o_idle.set(e["oil_pressure_idle_bar"]) self.o_slope.set(e["oil_pressure_slope_bar_per_krpm"]) self.o_floor.set(e["oil_pressure_off_floor_bar"]) self.plate_idle_min.set(e["throttle_plate_idle_min_pct"]) self.plate_overrun.set(e["throttle_plate_overrun_pct"]) self.plate_tau.set(e["throttle_plate_tau_s"]) self.torque_kp.set(e["torque_ctrl_kp"]) self.torque_ki.set(e["torque_ctrl_ki"]) self.jitter_idle.set(e["rpm_jitter_idle_amp_rpm"]) self.jitter_high.set(e["rpm_jitter_high_amp_rpm"]) self.jitter_tau.set(e["rpm_jitter_tau_s"]) self.jitter_off.set(e["rpm_jitter_off_threshold_rpm"]) self.pedal_var.set(e["throttle_pedal_pct"]) def apply(self): # Nur hier wird geschrieben cfg = {"engine": { "idle_rpm": int(self.idle_var.get()), "max_rpm": int(self.maxrpm_var.get()), "rpm_rise_per_s": int(self.rise_var.get()), "rpm_fall_per_s": int(self.fall_var.get()), "throttle_curve": self.thr_curve.get(), "engine_power_kw": float(self.power_kw.get()), "torque_peak_rpm": float(self.peak_rpm.get()), "starter_rpm_nominal": float(self.starter_nom.get()), "starter_voltage_min": float(self.starter_vmin.get()), "start_rpm_threshold": float(self.start_th.get()), "stall_rpm": float(self.stall_rpm.get()), "coolant_ambient_c": float(self.amb_c.get()), "coolant_warm_rate_c_per_s": float(self.c_warm.get()), "coolant_cool_rate_c_per_s": float(self.c_cool.get()), "oil_warm_rate_c_per_s": float(self.o_warm.get()), "oil_cool_rate_c_per_s": float(self.o_cool.get()), "idle_cold_gain_per_deg": float(self.cold_gain.get()), "idle_cold_gain_max": float(self.cold_gain_max.get()), "oil_pressure_idle_bar": float(self.o_idle.get()), "oil_pressure_slope_bar_per_krpm": float(self.o_slope.get()), "oil_pressure_off_floor_bar": float(self.o_floor.get()), "throttle_plate_idle_min_pct": float(self.plate_idle_min.get()), "throttle_plate_overrun_pct": float(self.plate_overrun.get()), "throttle_plate_tau_s": float(self.plate_tau.get()), "torque_ctrl_kp": float(self.torque_kp.get()), "torque_ctrl_ki": float(self.torque_ki.get()), "rpm_jitter_idle_amp_rpm": float(self.jitter_idle.get()), "rpm_jitter_high_amp_rpm": float(self.jitter_high.get()), "rpm_jitter_tau_s": float(self.jitter_tau.get()), "rpm_jitter_off_threshold_rpm": float(self.jitter_off.get()), "throttle_pedal_pct": float(self.pedal_var.get()), }} self.sim.load_config(cfg)