Komplette Umstellung auf V3.

This commit is contained in:
2024-07-15 11:41:52 +02:00
parent 1117fa90a6
commit eeedb5505a
5 changed files with 356 additions and 1006 deletions

474
main.py
View File

@@ -1,145 +1,383 @@
import tkinter as tk
from tkinter import messagebox
from tkinter import ttk, messagebox
import customtkinter
customtkinter.set_appearance_mode("light")
customtkinter.set_default_color_theme("blue")
import serial
import serial.tools.list_ports
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
#import random
import threading
import time
import numpy as np
p = 0.0
i = 0.0
set_torque = 0.0
act_torque = 0.0
act_angle = 0.0
class T2000ErdemGUI:
class ArduinoGUI:
def __init__(self, root):
self.my_font = customtkinter.CTkFont(family="Calibri", size=15)
self.root = root
self.root.title("T2000 Erdem")
menu_frame = tk.Frame(self.root)
menu_frame.grid(row=0, column=0, rowspan=8, columnspan=2, sticky="NESW", padx=10, pady=10)
menu_frame.columnconfigure(0, weight=1)
menu_frame.rowconfigure(6, weight=1)
plot_frame = tk.Frame(self.root)
plot_frame.grid(row=0, column=2, rowspan=8, sticky="NW")
plot_frame.rowconfigure(0, weight=1)
plot_frame.columnconfigure(0, weight=1)
self.root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight()))
self.root.title("PRG 342 GUI")
self.root.attributes('-fullscreen', True) # Setzt das Fenster in den Vollbildmodus
#self.root.overrideredirect(True) # Entfernt die Fensterdekorationen (wie Minimieren, Maximieren und Schließen)
# Eingabefelder für Koordinaten der Drehmoment-Drehwinkel-Kennlinie
tk.Label(menu_frame, text="Koordinaten der Drehmoment-Drehwinkel-Kennlinie:").grid(row=0, column=0, columnspan=2, pady=10, sticky="N")
self.coordinates_text = tk.Text(menu_frame, height=10, width=20)
self.coordinates_text.grid(row=1, column=0, columnspan=2, pady=10)
self.coordinates_text.insert('end', '0,0\n5,15\n10,10\n90,10')
self.arduino = None
self.ports = list(serial.tools.list_ports.comports())
self.running = True
self.current_point = None
self.setpoints = []
# Eingabefeld für direkte Drehmomentvorgabe
tk.Label(menu_frame, text="Soll-Drehmoment in Nm:").grid(row=2, column=0, pady=10, sticky="NW")
self.set_torque_value = tk.DoubleVar()
self.torque_input = tk.Entry(menu_frame, textvariable=self.set_torque_value)
self.torque_input.grid(row=2, column=1, sticky="NW", pady=10)
# Flags for user actions
self.setpoint_flag = False
self.multi_setpoints_flag = False
self.tare_flag = False
self.demag_flag = False
self.input_source_flag = False
self.pid_flag = False # Flag for sending PID parameters
# Anzeige für aktuellen Drehwinkel und Drehmoment
tk.Label(menu_frame, text="Aktueller Drehwinkel:").grid(row=3, column=0, pady=10, sticky="NW")
self.current_angle = tk.Label(menu_frame, text="0" + " °")
self.current_angle.grid(row=3, column=1, sticky="NW", pady=10)
self.pid_params = {}
self.selected_pid = None
tk.Label(menu_frame, text="Aktuelles Drehmoment:").grid(row=4, column=0, pady=10, sticky="NW")
self.current_torque = tk.Label(menu_frame, text="0" + " Nm")
self.current_torque.grid(row=4, column=1, sticky="NW", pady=10)
self.create_widgets()
# Button zum Schließen der Anwendung
self.close_button = tk.Button(menu_frame, text="Beenden", command=self.close_application)
self.close_button.grid(row=7, column=1, pady=10, sticky="S")
self.load_configurations()
# Button zur Übernahme der Eingaben und Aktualisierung des Diagramms
self.plot_button = tk.Button(menu_frame, text="Eingaben übernehmen", command=self.on_plot_button_click)
self.plot_button.grid(row=7, column=0, pady=10, sticky="S")
# Start the communication thread
self.communication_thread = threading.Thread(target=self.communication_loop)
self.communication_thread.start()
def create_widgets(self):
# Schalter für Umschaltung zwischen Drehmomentprofil und direkter Vorgabe
self.torque_mode = tk.BooleanVar()
self.torque_mode.set(False) # Startet im Modus "Drehmomentprofil"
self.torque_switch = tk.Checkbutton(menu_frame, text="Direkte Drehmomentvorgabe aktivieren", variable=self.torque_mode, command=self.toggle_torque_mode)
self.torque_switch.grid(row=5, column=0, columnspan=2, pady=10)
# Labels and ComboBox for Stellantriebstyp
self.stellantriebstyp_label = customtkinter.CTkLabel(self.root, text="Stellantriebstyp:", font=self.my_font)
self.stellantriebstyp_label.grid(row=0, column=0, padx=(10, 5), pady=(10, 0), sticky="w")
# Diagramm
self.figure = Figure(dpi=100)#(figsize=(6,6), dpi=100)
self.figure.set_figheight(self.root.winfo_screenheight()/100)
self.figure.set_figwidth((self.root.winfo_screenwidth()-380)/100)
self.plot = self.figure.add_subplot(111)
self.plot.set_xlim(0, 90)
self.plot.set_ylim(ymin=0)
self.plot.set_xlabel('Drehwinkel in Grad')
self.plot.set_ylabel('Drehmoment in Nm')
self.canvas = FigureCanvasTkAgg(self.figure, plot_frame)
self.canvas.get_tk_widget().pack(fill="both", expand=True)
self.config_combobox_value = customtkinter.StringVar()
self.config_combobox = customtkinter.CTkComboBox(self.root, variable=self.config_combobox_value, state="readonly", font=self.my_font)
self.config_combobox.grid(row=0, column=1, columnspan=1, padx=(0, 10), pady=(10, 0), sticky="ew")
#self.config_combobox.bind("<<ComboboxSelected>>", self.on_config_selected)
self.toggle_torque_mode()
self.create_diagram()
self.update_current_values_periodically()
# Setzen Button für Stellantriebstyp
self.set_pid_button = customtkinter.CTkButton(self.root, text="Setzen", command=self.set_pid_parameters, font=self.my_font)
self.set_pid_button.grid(row=0, column=2, padx=(0, 10), pady=(10, 0), sticky="ew")
def close_application(self):
#if messagebox.askokcancel("Beenden", "Möchten Sie die Anwendung wirklich beenden?"):
self.root.quit()
# Labels and ComboBox for COM Port
self.com_label = customtkinter.CTkLabel(self.root, text="COM Port:", font=self.my_font)
self.com_label.grid(row=1, column=0, padx=(10, 5), pady=(10, 0), sticky="w")
def create_diagram(self):
self.plot.clear()
self.plot.set_xlim(0, 90)
self.plot.set_xlabel('Drehwinkel in Grad')
self.plot.set_ylabel('Drehmoment in Nm')
coordinates = self.coordinates_text.get("1.0", tk.END).strip().split("\n")
x_coords, y_coords = [], []
for coord in coordinates:
x, y = map(float, coord.split(','))
x_coords.append(x)
y_coords.append(y)
self.plot.plot(x_coords, y_coords, marker='o', linestyle='-')
self.plot.set_ylim(bottom=0)
self.combobox_value = customtkinter.StringVar()
self.combobox = customtkinter.CTkComboBox(self.root, variable=self.combobox_value, font=self.my_font)
self.combobox.configure(values=[port.device for port in self.ports])
self.combobox.grid(row=1, column=1, padx=(0, 10), pady=(10, 0), sticky="ew")
self.connect_button = customtkinter.CTkButton(self.root, text="Verbinden", command=self.connect_arduino, font=self.my_font)
self.connect_button.grid(row=1, column=2, padx=(0, 10), pady=(10, 0), sticky="ew")
# Setpoint entry
self.setpoint_label = customtkinter.CTkLabel(self.root, text="Sollwerteingabe in Nm:", font=self.my_font)
self.setpoint_label.grid(row=2, column=0, padx=(10, 5), pady=(10, 0), sticky="w")
self.setpoint_entry = customtkinter.CTkEntry(self.root, font=self.my_font)
self.setpoint_entry.grid(row=2, column=1, padx=(0, 10), pady=(10, 0), sticky="ew")
self.set_setpoint_button = customtkinter.CTkButton(self.root, text="Setzen", command=self.set_setpoint, font=self.my_font)
self.set_setpoint_button.grid(row=2, column=2, padx=(0, 10), pady=(10, 0), sticky="ew")
# Multi Setpoints Textbox and Button
self.multi_setpoints_label = customtkinter.CTkLabel(self.root, text="Sollwerteeingabe\n(Winkel;Drehmoment):", font=self.my_font)
self.multi_setpoints_label.grid(row=3, column=0, padx=(10, 5), pady=(10, 0), sticky="w")
self.multi_setpoints_text = customtkinter.CTkTextbox(self.root, font=self.my_font)
self.multi_setpoints_text.grid(row=3, column=1, padx=(0, 10), pady=(10, 0), sticky="ew")
self.send_multi_setpoints_button = customtkinter.CTkButton(self.root, text="Sollwerte senden", command=self.send_multi_setpoints, font=self.my_font)
self.send_multi_setpoints_button.grid(row=3, column=2, padx=(0, 10), pady=(10, 0), sticky="ew")
# Analog Control CheckBox
self.checkbox_var = customtkinter.IntVar()
self.checkbox = customtkinter.CTkCheckBox(self.root, text="Analogsteuerung", variable=self.checkbox_var, command=self.input_source_Switch, font=self.my_font)
self.checkbox.grid(row=4, column=0, columnspan=3, padx=(10, 10), pady=(10, 0), sticky="w")
# Tare and Demagnetize Buttons
self.tare_button = customtkinter.CTkButton(self.root, text="Drehwinkel tarieren", command=self.tare_angle, font=self.my_font)
self.tare_button.grid(row=5, column=0, padx=(10, 5), pady=(10, 0), sticky="sew")
self.demag_button = customtkinter.CTkButton(self.root, text="Manuelle Entmagnetisierung", command=self.demagnetize, font=self.my_font)
self.demag_button.grid(row=5, column=1, padx=(0, 5), pady=(10, 0), sticky="sew")
# Exit Button
self.exit_button = customtkinter.CTkButton(self.root, text="Beenden", command=self.on_closing, font=self.my_font)
self.exit_button.grid(row=5, column=2, padx=(0, 10), pady=(10, 0), sticky="sew")
# Plot
self.figure, self.ax = plt.subplots()
self.canvas = FigureCanvasTkAgg(self.figure, self.root)
self.canvas.get_tk_widget().grid(row=0, column=3, rowspan=6, columnspan=4, padx=(10, 10), pady=(10, 0), sticky="nsew")
self.ax.set_title("Drehmoment-Drehwinkel-Kennlinie")
self.ax.set_xlabel("Winkel (°)")
self.ax.set_ylabel("Drehmoment (Nm)")
self.ax.set_xlim(0, 90)
self.ax.set_ylim(0, 50)
# Current readings labels
self.current_angle = 0
self.current_torque = 0
self.analogInput = 0
self.angle_label_var = customtkinter.StringVar()
self.angle_label_var.set("Drehwinkel: 0")
self.angle_label = customtkinter.CTkLabel(self.root, textvariable=self.angle_label_var, font=self.my_font)
self.angle_label.grid(row=6, column=3, padx=(10, 5), pady=(10, 0), sticky="sw")
self.torque_label_var = customtkinter.StringVar()
self.torque_label_var.set("Istwert: 0")
self.torque_label = customtkinter.CTkLabel(self.root, textvariable=self.torque_label_var, font=self.my_font)
self.torque_label.grid(row=6, column=4, padx=(10, 5), pady=(10, 0), sticky="sw")
self.analogread_label_var = customtkinter.StringVar()
self.analogread_label_var.set("Analogeingang: 0")
self.analogread_label = customtkinter.CTkLabel(self.root, textvariable=self.analogread_label_var, font=self.my_font)
self.analogread_label.grid(row=6, column=5, padx=(10, 5), pady=(10, 0), sticky="sw")
self.currentSetpoint_label_var = customtkinter.StringVar()
self.currentSetpoint_label_var.set("Aktueller Sollwert: 0")
self.currentSetpoint_label = customtkinter.CTkLabel(self.root, textvariable=self.currentSetpoint_label_var, font=self.my_font)
self.currentSetpoint_label.grid(row=6, column=6, padx=(10, 5), pady=(10, 0), sticky="sw")
self.root.grid_columnconfigure(0, weight=0)
self.root.grid_columnconfigure(1, weight=0)
self.root.grid_columnconfigure(2, weight=0)
self.root.grid_columnconfigure(3, weight=1)
self.root.grid_columnconfigure(4, weight=1)
self.root.grid_columnconfigure(5, weight=1)
self.root.grid_columnconfigure(6, weight=1)
self.root.grid_rowconfigure(0, weight=0)
self.root.grid_rowconfigure(1, weight=0)
self.root.grid_rowconfigure(2, weight=0)
self.root.grid_rowconfigure(3, weight=0)
self.root.grid_rowconfigure(4, weight=0)
self.root.grid_rowconfigure(5, weight=1)
self.root.grid_rowconfigure(6, weight=0)
def set_pid_parameters(self):
if self.arduino:
self.selected_pid = self.config_combobox.get()
if self.selected_pid in self.pid_params:
self.pid_flag = True
else:
messagebox.showerror("Error", "Selected PID configuration not found.")
else:
messagebox.showwarning("Warning", "Arduino not connected")
def load_configurations(self):
try:
with open("conf.txt", "r") as file:
config_name = None
for line in file:
line = line.strip()
if line.startswith("#") or not line:
continue
if "{" in line:
config_name = line.split("{")[0].strip()
self.pid_params[config_name] = {"Kp": 0.0, "Ki": 0.0, "Kd": 0.0}
elif "}" in line:
config_name = None
else:
if config_name:
key, value = line.split(":")
key = key.strip()
value = round(float(value.strip()), 3)
self.pid_params[config_name][key] = value
self.config_combobox.configure(values=list(self.pid_params.keys()))
if self.pid_params:
self.config_combobox.set(list(self.pid_params.keys())[0])
self.selected_pid = list(self.pid_params.keys())[0]
except Exception as e:
messagebox.showerror("Error", f"Error loading configurations: {e}")
def connect_arduino(self):
com_port = self.combobox_value.get()
if com_port:
try:
self.arduino = serial.Serial(com_port, 115200, timeout=1)
messagebox.showinfo("Info", "Connected to Arduino on " + com_port)
except Exception as e:
messagebox.showerror("Error", str(e))
else:
messagebox.showwarning("Warning", "Please select a COM port")
def set_setpoint(self):
if self.arduino:
try:
self.setpoint = float(self.setpoint_entry.get()) * 1000
self.setpoint_flag = True
except ValueError:
messagebox.showerror("Error", "Invalid setpoint value.")
else:
messagebox.showwarning("Warning", "Arduino not connected")
def send_multi_setpoints(self):
if self.arduino:
setpoints = self.multi_setpoints_text.get("1.0", tk.END).strip().split("\n")
coordinates = []
for point in setpoints:
try:
angle, torque = map(float, point.split(";"))
coordinates.append((angle, torque))
except ValueError:
messagebox.showerror("Error", f"Invalid input: {point}")
return
coordinates.sort()
self.setpoints = coordinates
self.plot_coordinates(coordinates)
self.multi_setpoints_flag = True
else:
messagebox.showwarning("Warning", "Arduino not connected")
def plot_coordinates(self, coordinates):
angles, torques = zip(*coordinates)
self.ax.clear()
self.ax.plot(angles, torques, 'bo-', label="Setpoints")
self.ax.set_title("Drehmoment-Drehwinkel-Kennlinie")
self.ax.set_xlabel("Winkel (°)")
self.ax.set_ylabel("Drehmoment (Nm)")
self.ax.set_xlim(0, 90)
self.ax.set_ylim(0, 50)
self.ax.legend()
self.canvas.draw()
def update_current_values(self, angle, act_torque):
self.current_angle.config(text=str(angle) + " °")
self.current_torque.config(text=str(act_torque) + " Nm")
def interpolate_coordinates(self, coordinates):
interpolated_points = []
for i in range(len(coordinates) - 1):
start_angle, start_torque = coordinates[i]
end_angle, end_torque = coordinates[i + 1]
num_points = int((end_angle - start_angle) * 2)
angles = np.linspace(start_angle, end_angle, num_points + 1)
torques = np.linspace(start_torque, end_torque, num_points + 1)
interpolated_points.extend(zip(angles, torques))
def save_set_values(self):
p = self.p_value.get()
i = self.i_value.get()
full_interpolated_points = [(angle, 0) for angle in np.arange(0, 90.5, 0.5)]
for angle, torque in interpolated_points:
index = int(angle * 2)
full_interpolated_points[index] = (angle, torque)
def toggle_torque_mode(self):
# Aktivieren oder Deaktivieren des direkten Drehmoment-Eingabefeldes
if self.torque_mode.get():
self.torque_input['state'] = 'normal'
self.coordinates_text['state'] = 'disabled'
return full_interpolated_points
def send_interpolated_points(self):
if self.arduino:
setpoints_command = "u"
for angle, torque in self.setpoints:
torque_mnm = torque * 1000
setpoints_command += f"{int(angle * 2)},{torque_mnm:.1f};"
setpoints_command = setpoints_command.rstrip(';') + "u\n"
self.arduino.write(setpoints_command.encode())
timeout_ctr = time.time()
while(self.arduino.in_waiting == 0 and time.time() - timeout_ctr < 2):
pass
try:
print(self.arduino.readline())
except Exception as e:
print(e)
def tare_angle(self):
if self.arduino:
self.tare_flag = True
else:
self.torque_input['state'] = 'disabled'
self.coordinates_text['state'] = 'normal'
messagebox.showwarning("Warning", "Arduino not connected")
def on_plot_button_click(self):
if self.torque_mode.get():
# Logik für die direkte Vorgabe des Soll-Drehmoments
set_torque = self.set_torque_value.get()
def demagnetize(self):
if self.arduino:
self.demag_flag = True
else:
# Logik für das Drehmomentprofil
self.create_diagram()
self.save_set_values()
messagebox.showwarning("Warning", "Arduino not connected")
def update_current_values_periodically(self):
# Simulieren von veränderlichen aktuellen Werten (als Beispiel)
#new_angle = random.randint(0, 90)
#new_torque = random.uniform(0, 50)
#self.update_current_values(new_angle, new_torque)
self.update_current_values(act_angle, act_torque)
self.root.after(200, self.update_current_values_periodically)
def input_source_Switch(self):
if self.arduino:
self.input_source_flag = True
else:
messagebox.showwarning("Warning", "Arduino not connected")
# Anwendung starten
root = tk.Tk()
app = T2000ErdemGUI(root)
root.mainloop()
def communication_loop(self):
while self.running:
if self.arduino:
self.arduino.read_all()
if self.setpoint_flag:
self.arduino.write(f"s{self.setpoint:.0f},0\n".encode())
self.setpoint_flag = False
time.sleep(0.1)
if self.multi_setpoints_flag:
self.send_interpolated_points()
self.multi_setpoints_flag = False
time.sleep(0.1)
if self.tare_flag:
self.arduino.write(b'w\n')
self.tare_flag = False
time.sleep(0.1)
if self.demag_flag:
self.arduino.write(b'e\n')
self.demag_flag = False
time.sleep(0.1)
if self.input_source_flag:
self.arduino.write(b'S\n')
self.input_source_flag = False
time.sleep(0.1)
if self.pid_flag:
self.send_pid_parameters()
self.pid_flag = False
self.arduino.write(b'a\n')
timeout_ctr = time.time()
while(self.arduino.in_waiting == 0 and time.time() - timeout_ctr < 2):
pass
try:
data = self.arduino.readline().decode(errors='ignore').strip().split(";")
self.current_angle = float(data[0]) / 1000
self.current_torque = float(data[1]) / 1000
self.analogInput = float(data[2]) / 1000
self.currentSetpoint = float(data[3]) / 1000
self.angle_label_var.set(f"Drehwinkel: {self.current_angle:.1f} °")
self.torque_label_var.set(f"Istwert: {self.current_torque:.1f} Nm")
self.analogread_label_var.set(f"Analogeingang: {self.analogInput:.1f} V")
self.currentSetpoint_label_var.set(f"Aktueller Sollwert: {self.currentSetpoint:.1f} Nm")
if self.current_point is not None:
self.current_point.remove()
self.current_point, = self.ax.plot([self.current_angle], [self.current_torque], 'ro')
self.canvas.draw()
except Exception as e:
print(e)
time.sleep(0.1)
def send_pid_parameters(self):
if self.selected_pid in self.pid_params:
pid_values = self.pid_params[self.selected_pid]
kp = f"{pid_values['Kp']:.3f}"
ki = f"{pid_values['Ki']:.3f}"
kd = f"{pid_values['Kd']:.3f}"
self.arduino.write(f"p{kp}\n".encode())
time.sleep(0.1)
self.arduino.write(f"i{ki}\n".encode())
time.sleep(0.1)
self.arduino.write(f"d{kd}\n".encode())
messagebox.showinfo("Info", f"PID parameters sent: Kp={kp}, Ki={ki}, Kd={kd}")
else:
messagebox.showerror("Error", "Selected PID configuration not found.")
def on_closing(self):
self.running = False
if self.arduino:
self.arduino.close()
self.root.quit()
self.root.destroy()
if __name__ == "__main__":
root = customtkinter.CTk()
app = ArduinoGUI(root)
root.protocol("WM_DELETE_WINDOW", app.on_closing)
root.mainloop()