Enhance TFTP server implementation with advanced monitoring and UI improvements
- Completely refactor TFTP server implementation with more robust file transfer handling - Add detailed transfer tracking with active transfers table - Implement periodic transfer status updates - Improve log and UI layout for TFTP server tab - Add more granular error handling and logging - Enhance threading and socket management for file transfers
This commit is contained in:
198
ComConfigCopy.py
198
ComConfigCopy.py
@@ -829,86 +829,202 @@ class SerialAppGUI(tk.Tk):
|
||||
about_window.grab_set()
|
||||
|
||||
def create_tftp_tab(self, frame):
|
||||
"""Создание вкладки TFTP сервера."""
|
||||
# Создаем фрейм для управления TFTP сервером
|
||||
control_frame = ttk.Frame(frame)
|
||||
control_frame.pack(fill=X, pady=5)
|
||||
|
||||
tftp_frame = ttk.Frame(frame)
|
||||
tftp_frame.pack(fill=BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
# Создаем и размещаем элементы управления
|
||||
controls_frame = ttk.LabelFrame(tftp_frame, text="Управление TFTP сервером")
|
||||
controls_frame.pack(fill=X, padx=5, pady=5)
|
||||
|
||||
# IP адрес
|
||||
ip_frame = ttk.Frame(control_frame)
|
||||
ip_frame.pack(fill=X, pady=5)
|
||||
ip_frame = ttk.Frame(controls_frame)
|
||||
ip_frame.pack(fill=X, padx=5, pady=2)
|
||||
ttk.Label(ip_frame, text="IP адрес:").pack(side=LEFT, padx=5)
|
||||
self.tftp_ip_var = StringVar(value="0.0.0.0")
|
||||
ttk.Entry(ip_frame, textvariable=self.tftp_ip_var, width=15).pack(side=LEFT, padx=5)
|
||||
|
||||
self.tftp_ip_entry = ttk.Entry(ip_frame, textvariable=self.tftp_ip_var)
|
||||
self.tftp_ip_entry.pack(fill=X, expand=True, padx=5)
|
||||
|
||||
# Порт
|
||||
port_frame = ttk.Frame(control_frame)
|
||||
port_frame.pack(fill=X, pady=5)
|
||||
port_frame = ttk.Frame(controls_frame)
|
||||
port_frame.pack(fill=X, padx=5, pady=2)
|
||||
ttk.Label(port_frame, text="Порт:").pack(side=LEFT, padx=5)
|
||||
self.tftp_port_var = StringVar(value="69")
|
||||
ttk.Entry(port_frame, textvariable=self.tftp_port_var, width=6).pack(side=LEFT, padx=5)
|
||||
|
||||
self.tftp_port_entry = ttk.Entry(port_frame, textvariable=self.tftp_port_var)
|
||||
self.tftp_port_entry.pack(fill=X, expand=True, padx=5)
|
||||
|
||||
# Кнопки управления
|
||||
button_frame = ttk.Frame(control_frame)
|
||||
button_frame.pack(fill=X, pady=5)
|
||||
self.start_tftp_button = ttk.Button(button_frame, text="Запустить сервер", command=self.start_tftp_server)
|
||||
self.start_tftp_button.pack(side=LEFT, padx=5)
|
||||
self.stop_tftp_button = ttk.Button(button_frame, text="Остановить сервер", command=self.stop_tftp_server, state="disabled")
|
||||
self.stop_tftp_button.pack(side=LEFT, padx=5)
|
||||
buttons_frame = ttk.Frame(controls_frame)
|
||||
buttons_frame.pack(fill=X, padx=5, pady=5)
|
||||
|
||||
# Лог TFTP сервера
|
||||
log_frame = ttk.Frame(frame)
|
||||
log_frame.pack(fill=BOTH, expand=True, pady=5)
|
||||
ttk.Label(log_frame, text="Лог сервера:").pack(anchor=W, padx=5)
|
||||
self.tftp_log_text = tk.Text(log_frame, wrap="word", height=15)
|
||||
self.start_tftp_button = ttk.Button(
|
||||
buttons_frame,
|
||||
text="Запустить сервер",
|
||||
command=self.start_tftp_server
|
||||
)
|
||||
self.start_tftp_button.pack(side=LEFT, padx=5)
|
||||
|
||||
self.stop_tftp_button = ttk.Button(
|
||||
buttons_frame,
|
||||
text="Остановить сервер",
|
||||
command=self.stop_tftp_server,
|
||||
state="disabled"
|
||||
)
|
||||
self.stop_tftp_button.pack(side=LEFT, padx=5)
|
||||
|
||||
# Лог сервера
|
||||
log_frame = ttk.LabelFrame(tftp_frame, text="Лог сервера")
|
||||
log_frame.pack(fill=BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
self.tftp_log_text = tk.Text(log_frame, wrap=tk.WORD, height=10)
|
||||
self.tftp_log_text.pack(fill=BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
# Добавляем скроллбар для лога
|
||||
scrollbar = ttk.Scrollbar(self.tftp_log_text, command=self.tftp_log_text.yview)
|
||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
self.tftp_log_text.config(yscrollcommand=scrollbar.set)
|
||||
|
||||
# Статус передач
|
||||
transfers_frame = ttk.LabelFrame(tftp_frame, text="Активные передачи")
|
||||
transfers_frame.pack(fill=BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
# Создаем таблицу для отображения активных передач
|
||||
columns = ("client", "filename", "progress", "remaining", "time")
|
||||
self.transfers_tree = ttk.Treeview(transfers_frame, columns=columns, show="headings")
|
||||
|
||||
# Настраиваем заголовки колонок
|
||||
self.transfers_tree.heading("client", text="Клиент")
|
||||
self.transfers_tree.heading("filename", text="Файл")
|
||||
self.transfers_tree.heading("progress", text="Прогресс")
|
||||
self.transfers_tree.heading("remaining", text="Осталось")
|
||||
self.transfers_tree.heading("time", text="Время")
|
||||
|
||||
# Настраиваем ширину колонок
|
||||
self.transfers_tree.column("client", width=120)
|
||||
self.transfers_tree.column("filename", width=150)
|
||||
self.transfers_tree.column("progress", width=100)
|
||||
self.transfers_tree.column("remaining", width=100)
|
||||
self.transfers_tree.column("time", width=80)
|
||||
|
||||
self.transfers_tree.pack(fill=BOTH, expand=True, padx=5, pady=5)
|
||||
|
||||
# Инициализация TFTP сервера
|
||||
self.tftp_server = None
|
||||
self.tftp_server_thread = None
|
||||
|
||||
def start_tftp_server(self):
|
||||
"""Запуск TFTP сервера."""
|
||||
try:
|
||||
ip = self.tftp_ip_var.get()
|
||||
port = int(self.tftp_port_var.get())
|
||||
|
||||
if not self.tftp_server:
|
||||
self.tftp_server = TFTPServer("Firmware")
|
||||
|
||||
# Устанавливаем функцию обратного вызова для логирования
|
||||
# Создаем экземпляр TFTP сервера
|
||||
self.tftp_server = TFTPServer("Firmware")
|
||||
|
||||
# Устанавливаем callback для логирования
|
||||
def log_callback(message):
|
||||
self.tftp_log_text.insert(END, f"{message}\n")
|
||||
self.tftp_log_text.see(END)
|
||||
self.append_tftp_log(message)
|
||||
# Обновляем информацию о передачах
|
||||
self.update_transfers_info()
|
||||
|
||||
self.tftp_server.set_log_callback(log_callback)
|
||||
|
||||
threading.Thread(
|
||||
# Запускаем сервер в отдельном потоке
|
||||
self.tftp_server_thread = threading.Thread(
|
||||
target=self.run_tftp_server,
|
||||
args=(ip, port),
|
||||
daemon=True
|
||||
).start()
|
||||
)
|
||||
self.tftp_server_thread.start()
|
||||
|
||||
# Обновляем состояние кнопок
|
||||
self.start_tftp_button.config(state="disabled")
|
||||
self.stop_tftp_button.config(state="normal")
|
||||
self.tftp_ip_entry.config(state="disabled")
|
||||
self.tftp_port_entry.config(state="disabled")
|
||||
|
||||
self.append_tftp_log(f"[INFO] TFTP сервер запущен на {ip}:{port}")
|
||||
|
||||
# Запускаем периодическое обновление информации о передачах
|
||||
self.update_transfers_periodically()
|
||||
|
||||
except ValueError:
|
||||
messagebox.showerror("Ошибка", "Некорректный порт")
|
||||
except Exception as e:
|
||||
self.append_tftp_log(f"[ERROR] Ошибка запуска сервера: {str(e)}")
|
||||
messagebox.showerror("Ошибка", f"Не удалось запустить TFTP сервер: {str(e)}")
|
||||
|
||||
|
||||
def run_tftp_server(self, ip, port):
|
||||
"""Запуск TFTP сервера в отдельном потоке."""
|
||||
try:
|
||||
self.tftp_server.start_server(ip, port)
|
||||
except Exception as e:
|
||||
self.tftp_log_text.insert(END, f"[ERROR] Ошибка TFTP сервера: {str(e)}\n")
|
||||
self.stop_tftp_server()
|
||||
|
||||
self.append_tftp_log(f"[ERROR] Ошибка работы сервера: {str(e)}")
|
||||
|
||||
def stop_tftp_server(self):
|
||||
"""Остановка TFTP сервера."""
|
||||
if self.tftp_server:
|
||||
try:
|
||||
self.tftp_server.stop_server()
|
||||
self.tftp_server = None
|
||||
self.tftp_log_text.insert(END, "[INFO] TFTP сервер остановлен\n")
|
||||
except Exception as e:
|
||||
self.tftp_log_text.insert(END, f"[ERROR] Ошибка при остановке TFTP сервера: {str(e)}\n")
|
||||
finally:
|
||||
if self.tftp_server_thread:
|
||||
self.tftp_server_thread.join(timeout=2.0)
|
||||
|
||||
# Обновляем состояние кнопок
|
||||
self.start_tftp_button.config(state="normal")
|
||||
self.stop_tftp_button.config(state="disabled")
|
||||
self.tftp_ip_entry.config(state="normal")
|
||||
self.tftp_port_entry.config(state="normal")
|
||||
|
||||
self.append_tftp_log("[INFO] TFTP сервер остановлен")
|
||||
|
||||
# Очищаем таблицу передач
|
||||
for item in self.transfers_tree.get_children():
|
||||
self.transfers_tree.delete(item)
|
||||
|
||||
except Exception as e:
|
||||
self.append_tftp_log(f"[ERROR] Ошибка остановки сервера: {str(e)}")
|
||||
messagebox.showerror("Ошибка", f"Не удалось остановить TFTP сервер: {str(e)}")
|
||||
|
||||
def append_tftp_log(self, message):
|
||||
"""Добавление сообщения в лог TFTP сервера."""
|
||||
self.tftp_log_text.insert(END, message + "\n")
|
||||
self.tftp_log_text.see(END)
|
||||
|
||||
def update_transfers_info(self):
|
||||
"""Обновление информации об активных передачах."""
|
||||
if not self.tftp_server:
|
||||
return
|
||||
|
||||
# Очищаем текущие записи
|
||||
for item in self.transfers_tree.get_children():
|
||||
self.transfers_tree.delete(item)
|
||||
|
||||
# Добавляем информацию о текущих передачах
|
||||
for client_addr, transfer_info in self.tftp_server.active_transfers.items():
|
||||
filename = transfer_info['filename']
|
||||
bytes_sent = transfer_info['bytes_sent']
|
||||
filesize = transfer_info['filesize']
|
||||
start_time = transfer_info['start_time']
|
||||
|
||||
# Вычисляем прогресс
|
||||
progress = f"{bytes_sent}/{filesize} байт"
|
||||
remaining = filesize - bytes_sent
|
||||
elapsed_time = time.time() - start_time
|
||||
|
||||
# Добавляем запись в таблицу
|
||||
self.transfers_tree.insert("", END, values=(
|
||||
f"{client_addr[0]}:{client_addr[1]}",
|
||||
filename,
|
||||
progress,
|
||||
f"{remaining} байт",
|
||||
f"{elapsed_time:.1f}с"
|
||||
))
|
||||
|
||||
def update_transfers_periodically(self):
|
||||
"""Периодическое обновление информации о передачах."""
|
||||
if self.tftp_server and self.tftp_server.running:
|
||||
self.update_transfers_info()
|
||||
# Планируем следующее обновление через 1 секунду
|
||||
self.after(1000, self.update_transfers_periodically)
|
||||
|
||||
# ==========================
|
||||
# Парсер аргументов (не используется)
|
||||
|
||||
Reference in New Issue
Block a user