210 lines
5.1 KiB
Python
210 lines
5.1 KiB
Python
import sys
|
|
import pandas as pd
|
|
import plotly.graph_objects as go
|
|
from plotly.subplots import make_subplots
|
|
import tkinter as tk
|
|
from tkinter import filedialog, messagebox
|
|
|
|
# --------------------------------------------------
|
|
# CSV einlesen
|
|
# --------------------------------------------------
|
|
def load_scope_csv(filename):
|
|
|
|
with open(filename, "r", encoding="utf-8", errors="ignore") as f:
|
|
lines = [line.strip() for line in f]
|
|
|
|
# Headerzeile suchen
|
|
header_idx = None
|
|
signal_names = []
|
|
|
|
for i, line in enumerate(lines):
|
|
if line.startswith("Name,ACTPOS"):
|
|
header_idx = i
|
|
cols = line.split(',')
|
|
|
|
# Signalnamen aus den "Name,<Signal>" Paaren holen
|
|
for j in range(0, len(cols), 2):
|
|
if j + 1 < len(cols):
|
|
signal_names.append(cols[j + 1])
|
|
|
|
break
|
|
|
|
if header_idx is None:
|
|
raise Exception("Signaldefinition nicht gefunden")
|
|
|
|
# Datenblock suchen
|
|
# Entweder folgt auf den Header ein <br>-Trenner (aeltere Scope-Exporte),
|
|
# oder die Daten folgen direkt nach dem Metadaten-Block. Im zweiten Fall
|
|
# starten wir einfach hinter dem Header; die Metadaten-Zeilen sowie ein
|
|
# moegliches EOF-Marker werden im numerischen Pruefschritt uebersprungen.
|
|
data_start = None
|
|
for i in range(header_idx + 1, len(lines)):
|
|
if lines[i].startswith("<br>"):
|
|
data_start = i + 1
|
|
break
|
|
|
|
if data_start is None:
|
|
data_start = header_idx + 1
|
|
|
|
numeric_rows = []
|
|
|
|
for line in lines[data_start:]:
|
|
if not line:
|
|
continue
|
|
|
|
if "<br>" in line:
|
|
continue
|
|
|
|
parts = line.split(",")
|
|
|
|
try:
|
|
float(parts[0])
|
|
numeric_rows.append(parts)
|
|
except:
|
|
continue
|
|
|
|
if not numeric_rows:
|
|
raise Exception("Keine Daten gefunden")
|
|
|
|
# Zeit + Kanäle aufbauen
|
|
time = []
|
|
signals = {name: [] for name in signal_names}
|
|
|
|
for row in numeric_rows:
|
|
|
|
time.append(float(row[0]))
|
|
|
|
idx = 1
|
|
for signal in signal_names:
|
|
signals[signal].append(float(row[idx]))
|
|
idx += 2
|
|
|
|
df = pd.DataFrame({"Time_ms": time})
|
|
|
|
for sig in signal_names:
|
|
df[sig] = signals[sig]
|
|
|
|
return df
|
|
|
|
|
|
# --------------------------------------------------
|
|
# Plot erstellen
|
|
# --------------------------------------------------
|
|
def create_plot():
|
|
|
|
selected = [name for name, var in signal_vars.items() if var.get()]
|
|
|
|
if not selected:
|
|
messagebox.showwarning("Hinweis", "Mindestens ein Signal auswählen.")
|
|
return
|
|
|
|
fig = go.Figure()
|
|
|
|
for signal in selected:
|
|
fig.add_trace(
|
|
go.Scatter(
|
|
x=df["Time_ms"],
|
|
y=df[signal],
|
|
mode="lines",
|
|
name=signal
|
|
)
|
|
)
|
|
|
|
# Y-Achse
|
|
if auto_scale_var.get():
|
|
y_range = None
|
|
else:
|
|
try:
|
|
ymin = float(ymin_entry.get())
|
|
ymax = float(ymax_entry.get())
|
|
y_range = [ymin, ymax]
|
|
except:
|
|
messagebox.showerror("Fehler", "Ungültige Y-Achsen Werte")
|
|
return
|
|
|
|
fig.update_layout(
|
|
title="TwinCAT Scope Daten",
|
|
xaxis_title="Zeit [ms]",
|
|
yaxis_title="Wert",
|
|
yaxis_range=y_range,
|
|
hovermode="x unified",
|
|
template="plotly_white"
|
|
)
|
|
|
|
fig.show()
|
|
|
|
|
|
# --------------------------------------------------
|
|
# Hauptprogramm
|
|
# --------------------------------------------------
|
|
def main():
|
|
global df, root, signal_vars, auto_scale_var, ymin_entry, ymax_entry
|
|
|
|
# Datei auswählen
|
|
if len(sys.argv) > 1:
|
|
# Dateipfad als Kommandozeilenargument uebergeben
|
|
filename = sys.argv[1]
|
|
else:
|
|
temp_root = tk.Tk()
|
|
temp_root.withdraw()
|
|
filename = filedialog.askopenfilename(
|
|
title="TwinCAT Scope CSV auswählen",
|
|
filetypes=[("CSV Dateien", "*.csv"), ("Alle Dateien", "*.*")]
|
|
)
|
|
temp_root.destroy()
|
|
|
|
if not filename:
|
|
raise SystemExit
|
|
|
|
df = load_scope_csv(filename)
|
|
|
|
# GUI
|
|
root = tk.Tk()
|
|
root.title("TwinCAT Scope Plotter")
|
|
|
|
signal_vars = {}
|
|
|
|
tk.Label(root, text="Signale auswählen").pack(anchor="w")
|
|
|
|
for col in df.columns[1:]:
|
|
var = tk.BooleanVar(value=True)
|
|
|
|
tk.Checkbutton(
|
|
root,
|
|
text=col,
|
|
variable=var
|
|
).pack(anchor="w")
|
|
|
|
signal_vars[col] = var
|
|
|
|
auto_scale_var = tk.BooleanVar(value=True)
|
|
|
|
tk.Checkbutton(
|
|
root,
|
|
text="Automatische Y-Skalierung",
|
|
variable=auto_scale_var
|
|
).pack(anchor="w", pady=10)
|
|
|
|
frame = tk.Frame(root)
|
|
frame.pack()
|
|
|
|
tk.Label(frame, text="Y-Min").grid(row=0, column=0)
|
|
ymin_entry = tk.Entry(frame, width=10)
|
|
ymin_entry.grid(row=0, column=1)
|
|
|
|
tk.Label(frame, text="Y-Max").grid(row=0, column=2)
|
|
ymax_entry = tk.Entry(frame, width=10)
|
|
ymax_entry.grid(row=0, column=3)
|
|
|
|
tk.Button(
|
|
root,
|
|
text="Plot anzeigen",
|
|
command=create_plot
|
|
).pack(pady=10)
|
|
|
|
root.mainloop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|