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_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_out_output_dir": "C:/src/____GitProjects/radar_data_reader/_rec",
"last_rec_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", "active_out_export_profile_name": "gsp_data",
"export_profiles": [ "export_profiles": [
{ {

View File

@ -16,6 +16,7 @@ from typing import List, Any, Dict, Tuple, Optional
from tkinter import filedialog, messagebox from tkinter import filedialog, messagebox
import tkinter as tk import tkinter as tk
import ctypes import ctypes
import threading
from ..utils.config_manager import ConfigManager from ..utils.config_manager import ConfigManager
from ..core.file_reader import run_worker_process from ..core.file_reader import run_worker_process
@ -532,3 +533,71 @@ class AppController:
self.worker_process.terminate() self.worker_process.terminate()
self._close_all_files() self._close_all_files()
logger.shutdown_logging_system() 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.batches_found_var = tk.StringVar(value="N/A")
self.progress_bar_var = tk.DoubleVar(value=0) 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): def _create_widgets(self):
menu_bar = tk.Menu(self.master) menu_bar = tk.Menu(self.master)
self.master.config(menu=menu_bar) 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.out_processor_tab = ttk.Frame(self.notebook, padding="10")
self.rec_converter_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.out_processor_tab, text="OUT Processor")
self.notebook.add(self.rec_converter_tab, text="REC to OUT Converter") self.notebook.add(self.rec_converter_tab, text="REC to OUT Converter")
self._create_out_processor_tab(self.out_processor_tab) self._create_out_processor_tab(self.out_processor_tab)
self._create_rec_converter_tab(self.rec_converter_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) 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) 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): def _create_out_processor_tab(self, parent):
parent.columnconfigure(1, weight=1) parent.columnconfigure(1, weight=1)

View File

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