draft add flight analyzer tab

This commit is contained in:
VALLONGOL 2025-06-25 15:42:01 +02:00
parent db0e26b5a1
commit 06b1fe79e1
4 changed files with 155 additions and 0 deletions

View File

@ -3,6 +3,7 @@
"last_opened_rec_file": "C:/src/____GitProjects/radar_data_reader/_rec/_25-05-15-12-22-52_sata_345.rec",
"last_out_output_dir": "C:/src/____GitProjects/radar_data_reader/_rec",
"last_rec_output_dir": "C:\\src\\____GitProjects\\radar_data_reader\\_rec",
"last_flight_folder": "//tsclient/F/__DATI_VOLI/Volo12 - Maggio 2025/_rec",
"active_out_export_profile_name": "gsp_data",
"export_profiles": [
{

View File

@ -16,6 +16,7 @@ from typing import List, Any, Dict, Tuple, Optional
from tkinter import filedialog, messagebox
import tkinter as tk
import ctypes
import threading
from ..utils.config_manager import ConfigManager
from ..core.file_reader import run_worker_process
@ -532,3 +533,71 @@ class AppController:
self.worker_process.terminate()
self._close_all_files()
logger.shutdown_logging_system()
def select_and_analyze_flight_folder(self):
"""
Opens a dialog to select a folder and then starts the analysis
in a separate thread to keep the GUI responsive.
"""
initial_dir = self.config_manager.get("last_flight_folder")
if new_dir := filedialog.askdirectory(
initialdir=initial_dir, title="Select Folder with Flight Recordings"
):
# --- Update GUI immediately to show something is happening ---
self.view.analyzer_rec_folder_var.set(new_dir)
self.view.analyzer_info_var.set("Scanning folder, please wait...")
self.view.start_analysis_button.config(state=tk.DISABLED)
# Force GUI update
self.view.update_idletasks()
# --- Run the slow analysis in a background thread ---
analysis_thread = threading.Thread(
target=self._analyze_folder_worker,
args=(new_dir,),
daemon=True
)
analysis_thread.start()
def _analyze_folder_worker(self, dir_path_str: str):
"""
Worker thread function to perform the slow task of scanning folder contents.
"""
try:
folder_path = Path(dir_path_str)
self.config_manager.set("last_flight_folder", dir_path_str)
self.config_manager.save_config()
rec_files = sorted([f for f in folder_path.glob("*.rec")])
if not rec_files:
self.view.analyzer_info_var.set("No .rec files found in the selected folder.")
# The button remains disabled, which is correct
return
total_size_bytes = sum(f.stat().st_size for f in rec_files)
total_size_mb = total_size_bytes / (1024 * 1024)
file_count = len(rec_files)
info_text = (
f"Found {file_count} .rec files, "
f"Total size: {total_size_mb:.2f} MB. Ready for analysis."
)
self.view.analyzer_info_var.set(info_text)
# Generate default flight name
first_file_name = rec_files[0].stem
match = re.search(r"(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})", first_file_name)
if match:
yy, mo, dd, hh, mi, ss = match.groups()
flight_name = f"{yy}{mo}{dd}_{hh}{mi}{ss}_Flight"
self.view.analyzer_flight_name_var.set(flight_name)
else:
self.view.analyzer_flight_name_var.set(f"{folder_path.name}_Flight")
# Enable the analysis button on the main thread
self.view.start_analysis_button.config(state=tk.NORMAL)
except Exception as e:
log.error(f"Error in folder analysis worker: {e}", exc_info=True)
self.view.analyzer_info_var.set(f"Error during folder analysis: {e}")
self.view.start_analysis_button.config(state=tk.DISABLED)

View File

@ -76,6 +76,10 @@ class MainWindow(tk.Frame):
self.batches_found_var = tk.StringVar(value="N/A")
self.progress_bar_var = tk.DoubleVar(value=0)
self.analyzer_rec_folder_var = tk.StringVar()
self.analyzer_flight_name_var = tk.StringVar()
self.analyzer_info_var = tk.StringVar(value="Please select a folder and a flight name.")
def _create_widgets(self):
menu_bar = tk.Menu(self.master)
self.master.config(menu=menu_bar)
@ -98,12 +102,19 @@ class MainWindow(tk.Frame):
self.out_processor_tab = ttk.Frame(self.notebook, padding="10")
self.rec_converter_tab = ttk.Frame(self.notebook, padding="10")
# --- NUOVA RIGA ---
self.flight_analyzer_tab = ttk.Frame(self.notebook, padding="10")
# --- ORDINE MODIFICATO ---
# Mettiamo il nuovo wizard come prima tab perché è il punto di partenza del workflow
self.notebook.add(self.flight_analyzer_tab, text="Flight Analyzer")
self.notebook.add(self.out_processor_tab, text="OUT Processor")
self.notebook.add(self.rec_converter_tab, text="REC to OUT Converter")
self._create_out_processor_tab(self.out_processor_tab)
self._create_rec_converter_tab(self.rec_converter_tab)
# --- NUOVA CHIAMATA ---
self._create_flight_analyzer_tab(self.flight_analyzer_tab)
self._create_log_console_frame(main_frame)
@ -116,6 +127,79 @@ class MainWindow(tk.Frame):
)
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
def _create_flight_analyzer_tab(self, parent):
"""Creates the widgets for the new Flight Analyzer wizard tab."""
parent.columnconfigure(0, weight=1)
# La riga 2 conterrà la timeline, quindi le diamo peso per espandersi
parent.rowconfigure(2, weight=1)
# --- Frame 1: Input e Setup del Volo ---
setup_frame = ttk.LabelFrame(parent, text="Flight Setup")
setup_frame.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
setup_frame.columnconfigure(1, weight=1)
# Selezione cartella REC
ttk.Label(setup_frame, text="Recordings Folder:").grid(
row=0, column=0, padx=5, pady=5, sticky="w"
)
rec_folder_entry = ttk.Entry(
setup_frame, textvariable=self.analyzer_rec_folder_var, state="readonly"
)
rec_folder_entry.grid(row=0, column=1, sticky="ew", padx=5)
ttk.Button(
setup_frame, text="Browse...", command=self.controller.select_and_analyze_flight_folder).grid(row=0, column=2, padx=5)
# Nome del Volo
ttk.Label(setup_frame, text="Flight Name:").grid(
row=1, column=0, padx=5, pady=5, sticky="w"
)
flight_name_entry = ttk.Entry(setup_frame, textvariable=self.analyzer_flight_name_var)
flight_name_entry.grid(row=1, column=1, columnspan=2, sticky="ew", padx=5)
# --- Frame 2: Azioni e Informazioni ---
action_frame = ttk.Frame(parent)
action_frame.grid(row=1, column=0, sticky="ew", padx=5, pady=10)
self.start_analysis_button = ttk.Button(
action_frame, text="Start Flight Analysis", command=lambda: print("TODO: Start Analysis"),
state=tk.DISABLED
)
self.start_analysis_button.pack(side=tk.LEFT)
# Area per info preliminari
info_label = ttk.Label(action_frame, textvariable=self.analyzer_info_var)
info_label.pack(side=tk.LEFT, padx=20)
# --- Frame 3: Risultati e Timeline ---
results_frame = ttk.LabelFrame(parent, text="Flight Summary & Segments")
results_frame.grid(row=2, column=0, sticky="nsew", padx=5, pady=5)
results_frame.columnconfigure(0, weight=1)
results_frame.rowconfigure(0, weight=1)
# Tabella per la timeline
self.flight_timeline_tree = ttk.Treeview(
results_frame,
columns=("mode", "start_batch", "end_batch", "duration"),
show="headings"
)
self.flight_timeline_tree.heading("mode", text="Mode")
self.flight_timeline_tree.heading("start_batch", text="Start Batch")
self.flight_timeline_tree.heading("end_batch", text="End Batch")
self.flight_timeline_tree.heading("duration", text="Duration (s)")
self.flight_timeline_tree.grid(row=0, column=0, sticky="nsew")
# Scrollbar per la tabella
tree_scrollbar = ttk.Scrollbar(results_frame, orient="vertical", command=self.flight_timeline_tree.yview)
self.flight_timeline_tree.configure(yscrollcommand=tree_scrollbar.set)
tree_scrollbar.grid(row=0, column=1, sticky="ns")
# Bottone per esportare i segmenti
self.export_segment_button = ttk.Button(
results_frame, text="Export Selected Segment(s)", state=tk.DISABLED, command=lambda: print("TODO: Export Segment") # TODO
)
self.export_segment_button.grid(row=1, column=0, columnspan=2, pady=5)
def _create_out_processor_tab(self, parent):
parent.columnconfigure(1, weight=1)

View File

@ -79,6 +79,7 @@ class ConfigManager:
"last_opened_rec_file": "",
"last_out_output_dir": "",
"last_rec_output_dir": "",
"last_flight_folder": "",
"active_out_export_profile_name": "Default",
"export_profiles": [default_export_profile.to_dict()],
"cpp_converter_config": default_cpp_config,