S1005403_RisCC/target_simulator/gui/settings_window.py

228 lines
8.2 KiB
Python

# target_simulator/gui/settings_window.py
"""
A Toplevel window for configuring connection settings (Serial or TFTP).
"""
import tkinter as tk
from tkinter import ttk, messagebox
from typing import Dict, Any
from target_simulator.core.serial_communicator import SerialCommunicator, SERIAL_PARAMS
from target_simulator.core.tftp_communicator import TFTPCommunicator
from target_simulator.utils.config_manager import ConfigManager
class SettingsWindow(tk.Toplevel):
"""A dialog window for connection settings."""
def __init__(
self, master, config_manager: ConfigManager, current_config: Dict[str, Any]
):
super().__init__(master)
self.master_view = master
self.config_manager = config_manager
self.title("Connection Settings")
self.transient(master)
self.grab_set()
self.resizable(False, False)
# --- Variables ---
self.comm_type_var = tk.StringVar(value=current_config.get("type", "serial"))
self.serial_config_vars = self._setup_serial_vars(
current_config.get("serial", {})
)
self.tftp_config_vars = self._setup_tftp_vars(current_config.get("tftp", {}))
# --- UI Creation ---
main_frame = ttk.Frame(self, padding="10")
main_frame.pack(fill=tk.BOTH, expand=True)
self._create_type_selector(main_frame)
# --- Configuration Frames ---
self.serial_frame = self._create_serial_frame(main_frame)
self.tftp_frame = self._create_tftp_frame(main_frame)
self._create_buttons(main_frame)
# --- Initial State ---
self._on_comm_type_change()
self.protocol("WM_DELETE_WINDOW", self.destroy)
self.wait_window(self)
def _setup_serial_vars(self, config: Dict) -> Dict[str, tk.StringVar]:
return {
"port": tk.StringVar(value=config.get("port", "")),
"baudrate": tk.StringVar(value=config.get("baudrate", "115200")),
"bytesize": tk.StringVar(value=config.get("bytesize", "8")),
"parity": tk.StringVar(value=config.get("parity", "N")),
"stopbits": tk.StringVar(value=config.get("stopbits", "1")),
}
def _setup_tftp_vars(self, config: Dict) -> Dict[str, tk.StringVar]:
return {
"ip": tk.StringVar(value=config.get("ip", "127.0.0.1")),
"port": tk.StringVar(value=config.get("port", "69")),
}
def _create_type_selector(self, parent):
frame = ttk.LabelFrame(parent, text="Communication Type", padding=10)
frame.pack(fill=tk.X, pady=(0, 10))
ttk.Radiobutton(
frame,
text="Serial",
variable=self.comm_type_var,
value="serial",
command=self._on_comm_type_change,
).pack(side=tk.LEFT, padx=10)
ttk.Radiobutton(
frame,
text="TFTP",
variable=self.comm_type_var,
value="tftp",
command=self._on_comm_type_change,
).pack(side=tk.LEFT, padx=10)
def _create_serial_frame(self, parent) -> ttk.Frame:
frame = ttk.LabelFrame(parent, text="Serial Port Settings", padding=10)
# Grid layout for alignment
frame.columnconfigure(1, weight=1)
# Port
ttk.Label(frame, text="Port:").grid(row=0, column=0, sticky=tk.W, pady=2)
port_combo = ttk.Combobox(
frame, textvariable=self.serial_config_vars["port"], state="readonly"
)
port_combo.grid(row=0, column=1, sticky=tk.EW, padx=5, pady=2)
self._populate_ports(port_combo)
# Baudrate
ttk.Label(frame, text="Baud Rate:").grid(row=1, column=0, sticky=tk.W, pady=2)
baud_combo = ttk.Combobox(
frame,
textvariable=self.serial_config_vars["baudrate"],
values=SERIAL_PARAMS["baudrates"],
state="readonly",
)
baud_combo.grid(row=1, column=1, sticky=tk.EW, padx=5, pady=2)
# Parity
ttk.Label(frame, text="Parity:").grid(row=2, column=0, sticky=tk.W, pady=2)
parity_combo = ttk.Combobox(
frame,
textvariable=self.serial_config_vars["parity"],
values=list(SERIAL_PARAMS["parities"]),
state="readonly",
)
parity_combo.grid(row=2, column=1, sticky=tk.EW, padx=5, pady=2)
return frame
def _create_tftp_frame(self, parent) -> ttk.Frame:
frame = ttk.LabelFrame(parent, text="TFTP Server Settings", padding=10)
frame.columnconfigure(1, weight=1)
# IP Address
ttk.Label(frame, text="Server IP:").grid(row=0, column=0, sticky=tk.W, pady=2)
ttk.Entry(frame, textvariable=self.tftp_config_vars["ip"]).grid(
row=0, column=1, sticky=tk.EW, padx=5, pady=2
)
# Port
ttk.Label(frame, text="Server Port:").grid(row=1, column=0, sticky=tk.W, pady=2)
ttk.Entry(frame, textvariable=self.tftp_config_vars["port"]).grid(
row=1, column=1, sticky=tk.EW, padx=5, pady=2
)
return frame
def _create_buttons(self, parent):
frame = ttk.Frame(parent, padding=(0, 10, 0, 0))
frame.pack(fill=tk.X)
ttk.Button(
frame, text="Test Connection", command=self._on_test_connection
).pack(side=tk.LEFT, padx=(0, 10))
# Right-align Save and Cancel
ttk.Button(frame, text="Cancel", command=self.destroy).pack(side=tk.RIGHT)
ttk.Button(frame, text="Save", command=self._on_save).pack(
side=tk.RIGHT, padx=5
)
def _on_comm_type_change(self):
"""Show/hide frames based on the selected communication type."""
selected_type = self.comm_type_var.get()
if selected_type == "serial":
self.tftp_frame.pack_forget()
self.serial_frame.pack(fill=tk.X, expand=True)
elif selected_type == "tftp":
self.serial_frame.pack_forget()
self.tftp_frame.pack(fill=tk.X, expand=True)
def _populate_ports(self, combobox: ttk.Combobox):
"""Fetches and displays available serial ports."""
available_ports = SerialCommunicator.list_available_ports()
if available_ports:
combobox["values"] = available_ports
if not self.serial_config_vars["port"].get():
self.serial_config_vars["port"].set(available_ports[0])
else:
combobox["values"] = ["No ports found"]
self.serial_config_vars["port"].set("No ports found")
def _on_test_connection(self):
"""Tests the connection for the currently selected mode."""
comm_type = self.comm_type_var.get()
is_success = False
config = {}
if comm_type == "serial":
config = {key: var.get() for key, var in self.serial_config_vars.items()}
is_success = SerialCommunicator.test_connection(config)
else: # tftp
config = {key: var.get() for key, var in self.tftp_config_vars.items()}
is_success = TFTPCommunicator.test_connection(config)
if is_success:
messagebox.showinfo(
"Success",
f"Connection test successful for {comm_type.upper()}.",
parent=self,
)
else:
messagebox.showerror(
"Failure",
f"Failed to connect using {comm_type.upper()} settings.",
parent=self,
)
def _on_save(self):
"""Saves the selected settings and closes the window."""
comm_type = self.comm_type_var.get()
# Recupera la configurazione corrente
current = (
self.master_view.connection_config
if hasattr(self.master_view, "connection_config")
else {}
)
serial_conf = current.get("serial", {})
tftp_conf = current.get("tftp", {})
if comm_type == "serial":
serial_conf = {
key: var.get() for key, var in self.serial_config_vars.items()
}
else:
tftp_conf = {key: var.get() for key, var in self.tftp_config_vars.items()}
full_config = {"type": comm_type, "serial": serial_conf, "tftp": tftp_conf}
self.master_view.update_connection_settings(full_config)
self.destroy()