109 lines
5.4 KiB
Python
109 lines
5.4 KiB
Python
# app/gui/can_panel.py
|
|
from __future__ import annotations
|
|
import json, subprocess, tkinter as tk
|
|
from tkinter import ttk, messagebox
|
|
from app.can import (
|
|
list_can_ifaces, link_up, link_down, link_state, link_kind, have_cap_netadmin
|
|
)
|
|
|
|
class CanPanel:
|
|
"""Fixes CAN- & Responder-Panel (oben links)."""
|
|
def __init__(self, parent, responder, initial_iface: str, initial_resp_id: int,
|
|
initial_timeout_ms: int, initial_bitrate: int, on_rebind_iface=None):
|
|
self.responder = responder
|
|
self.on_rebind_iface = on_rebind_iface or (lambda iface: None)
|
|
|
|
self.frame = ttk.LabelFrame(parent, text="CAN & Settings", padding=8)
|
|
for i in range(3): self.frame.columnconfigure(i, weight=1)
|
|
|
|
# Interface
|
|
ttk.Label(self.frame, text="Interface").grid(row=0, column=0, sticky="w")
|
|
self.iface_var = tk.StringVar(value=initial_iface)
|
|
self.iface_dd = ttk.Combobox(self.frame, textvariable=self.iface_var,
|
|
values=list_can_ifaces() or [initial_iface],
|
|
state="readonly", width=14)
|
|
self.iface_dd.grid(row=0, column=1, sticky="ew", padx=(6,0))
|
|
ttk.Button(self.frame, text="Refresh", command=self._refresh_ifaces).grid(row=0, column=2, sticky="w")
|
|
|
|
# RESP-ID
|
|
ttk.Label(self.frame, text="RESP-ID (hex)").grid(row=1, column=0, sticky="w")
|
|
self.resp_var = tk.StringVar(value=f"0x{initial_resp_id:03X}")
|
|
ttk.Entry(self.frame, textvariable=self.resp_var, width=10).grid(row=1, column=1, sticky="w", padx=(6,0))
|
|
|
|
# Timeout
|
|
ttk.Label(self.frame, text="Timeout (ms)").grid(row=2, column=0, sticky="w")
|
|
self.to_var = tk.IntVar(value=int(initial_timeout_ms))
|
|
ttk.Spinbox(self.frame, from_=10, to=5000, increment=10, textvariable=self.to_var, width=10)\
|
|
.grid(row=2, column=1, sticky="w", padx=(6,0))
|
|
|
|
# Bitrate
|
|
ttk.Label(self.frame, text="Bitrate").grid(row=3, column=0, sticky="w")
|
|
self.br_var = tk.IntVar(value=int(initial_bitrate))
|
|
ttk.Spinbox(self.frame, from_=20000, to=1000000, increment=10000, textvariable=self.br_var, width=12)\
|
|
.grid(row=3, column=1, sticky="w", padx=(6,0))
|
|
|
|
self.set_params = tk.BooleanVar(value=True)
|
|
ttk.Checkbutton(self.frame, text="Bitrate beim UP setzen", variable=self.set_params)\
|
|
.grid(row=3, column=2, sticky="w")
|
|
|
|
self.kind_label = ttk.Label(self.frame, text=f"Kind: {link_kind(initial_iface)}", style="Small.TLabel")
|
|
self.kind_label.grid(row=4, column=0, columnspan=3, sticky="w", pady=(4,0))
|
|
|
|
# Buttons
|
|
btns = ttk.Frame(self.frame); btns.grid(row=5, column=0, columnspan=3, sticky="ew", pady=(8,0))
|
|
ttk.Button(btns, text="Link UP", command=self._do_link_up).grid(row=0, column=0, sticky="w")
|
|
ttk.Button(btns, text="Link DOWN", command=self._do_link_down).grid(row=0, column=1, sticky="w", padx=(6,0))
|
|
ttk.Button(btns, text="Responder Rebind", command=self._do_rebind).grid(row=0, column=2, sticky="w", padx=(12,0))
|
|
|
|
ttk.Label(self.frame, text=f"CAP_NET_ADMIN: {'yes' if have_cap_netadmin() else 'no'}",
|
|
style="Small.TLabel").grid(row=6, column=0, columnspan=3, sticky="w", pady=(6,0))
|
|
|
|
# ---- actions ----
|
|
def _refresh_ifaces(self):
|
|
lst = list_can_ifaces()
|
|
if not lst:
|
|
messagebox.showwarning("Interfaces", "Keine can*/vcan* Interfaces gefunden.")
|
|
return
|
|
self.iface_dd.config(values=lst)
|
|
|
|
def _do_link_up(self):
|
|
iface = self.iface_var.get()
|
|
try:
|
|
self.kind_label.config(text=f"Kind: {link_kind(iface)}")
|
|
if link_state(iface) == "UP":
|
|
messagebox.showinfo("CAN", f"{iface} ist bereits UP"); return
|
|
link_up(iface, bitrate=self.br_var.get(), fd=False, set_params=self.set_params.get())
|
|
try:
|
|
out = subprocess.check_output(["ip","-details","-json","link","show",iface], text=True)
|
|
info = json.loads(out)[0]
|
|
bt = (info.get("linkinfo", {}) or {}).get("info_data", {}).get("bittiming") or {}
|
|
br = bt.get("bitrate"); sp = bt.get("sample-point")
|
|
if br:
|
|
messagebox.showinfo("CAN", f"{iface} ist UP @ {br} bit/s (SP {sp})")
|
|
except Exception:
|
|
pass
|
|
except Exception as e:
|
|
messagebox.showerror("CAN", f"Link UP fehlgeschlagen:\n{e}")
|
|
|
|
def _do_link_down(self):
|
|
iface = self.iface_var.get()
|
|
try:
|
|
if link_state(iface) == "DOWN":
|
|
messagebox.showinfo("CAN", f"{iface} ist bereits DOWN"); return
|
|
link_down(iface); messagebox.showinfo("CAN", f"{iface} ist DOWN")
|
|
except Exception as e:
|
|
messagebox.showerror("CAN", f"Link DOWN fehlgeschlagen:\n{e}")
|
|
|
|
def _do_rebind(self):
|
|
iface = self.iface_var.get()
|
|
try:
|
|
new_resp = int(self.resp_var.get(), 16)
|
|
except Exception:
|
|
messagebox.showerror("RESP-ID", "Bitte gültige Hex-Zahl, z.B. 0x7E8"); return
|
|
try:
|
|
self.responder.rebind(interface=iface, resp_id=new_resp, timeout_ms=self.to_var.get())
|
|
self.on_rebind_iface(iface) # TraceView umhängen
|
|
messagebox.showinfo("CAN", f"Responder neu gebunden: {iface}, RESP 0x{new_resp:03X}")
|
|
except Exception as e:
|
|
messagebox.showerror("CAN", f"Rebind fehlgeschlagen:\n{e}")
|