draft add flight analyzer tab
This commit is contained in:
parent
db0e26b5a1
commit
06b1fe79e1
@ -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": [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -75,6 +75,10 @@ class MainWindow(tk.Frame):
|
|||||||
self.progress_text_var = tk.StringVar(value="N/A")
|
self.progress_text_var = tk.StringVar(value="N/A")
|
||||||
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)
|
||||||
@ -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)
|
||||||
|
|
||||||
@ -115,6 +126,79 @@ class MainWindow(tk.Frame):
|
|||||||
padding=2,
|
padding=2,
|
||||||
)
|
)
|
||||||
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)
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user