# FlightMonitor/controller/cleanup_manager.py """ Manages the orderly shutdown and cleanup of application resources, including closing database connections, stopping background threads, and destroying secondary GUI windows upon application exit or specific events. """ from typing import Any, Optional, TYPE_CHECKING from ..utils.logger import get_logger # Type checking imports to avoid circular dependencies at runtime if TYPE_CHECKING: from .app_controller import AppController from ..data.storage import DataStorage from ..data.aircraft_database_manager import AircraftDatabaseManager from ..gui.main_window import MainWindow from ..gui.dialogs.full_flight_details_window import FullFlightDetailsWindow from ..map.map_canvas_manager import MapCanvasManager module_logger = get_logger(__name__) class CleanupManager: """ Handles all application shutdown and resource cleanup operations. It orchestrates the closing of various modules and components. """ def __init__(self, app_controller: "AppController"): """ Initializes the CleanupManager. Args: app_controller: The main AppController instance, providing access to all application components that need cleanup. """ self.app_controller = app_controller module_logger.debug("CleanupManager initialized.") def on_application_exit(self): """ Performs a coordinated shutdown of all application resources. This method is called when the main application window is closing. """ module_logger.info("CleanupManager: Application exit requested. Cleaning up resources.") # --- Close active Full Flight Details window (if any) --- # Access active_detail_window_ref and active_detail_window_icao via app_controller if ( self.app_controller.active_detail_window_ref and self.app_controller.active_detail_window_ref.winfo_exists() ): try: module_logger.info( f"CleanupManager: Closing active detail window for {self.app_controller.active_detail_window_icao} on app exit." ) self.app_controller.active_detail_window_ref.destroy() except Exception as e_close_detail: module_logger.error( f"CleanupManager: Error closing detail window on app exit: {e_close_detail}" ) finally: # Clear references in AppController directly or via a method self.app_controller.active_detail_window_ref = None self.app_controller.active_detail_window_icao = None # --- Shutdown MapCanvasManager worker (if active) --- # Access map_manager_instance via app_controller.main_window main_window = self.app_controller.main_window if ( main_window and hasattr(main_window, "map_manager_instance") and main_window.map_manager_instance is not None ): map_manager: "MapCanvasManager" = main_window.map_manager_instance if hasattr(map_manager, "shutdown_worker") and callable(map_manager.shutdown_worker): try: map_manager.shutdown_worker() module_logger.info("CleanupManager: Main MapCanvasManager worker shutdown requested.") except Exception as e_map_shutdown: module_logger.error( f"CleanupManager: Error during Main MapCanvasManager worker shutdown: {e_map_shutdown}", exc_info=True, ) # --- Stop live monitoring (adapter thread and data processor) --- # Access live_adapter_thread, is_live_monitoring_active via app_controller # Access stop_live_monitoring via app_controller is_adapter_considered_running = ( self.app_controller.live_adapter_thread and self.app_controller.live_adapter_thread.is_alive() ) or self.app_controller.is_live_monitoring_active if is_adapter_considered_running: module_logger.info("CleanupManager: Live monitoring is active, requesting AppController to stop it.") self.app_controller.stop_live_monitoring(from_error=False) # --- Close DataStorage connection --- # Access data_storage via app_controller if self.app_controller.data_storage: try: self.app_controller.data_storage.close_connection() module_logger.info("CleanupManager: DataStorage connection closed.") except Exception as e_db_close: module_logger.error( f"CleanupManager: Error closing DataStorage: {e_db_close}", exc_info=True ) finally: self.app_controller.data_storage = None # Clear reference in AppController # --- Close AircraftDatabaseManager connection --- # Access aircraft_db_manager via app_controller if self.app_controller.aircraft_db_manager: try: self.app_controller.aircraft_db_manager.close_connection() module_logger.info("CleanupManager: AircraftDatabaseManager connection closed.") except Exception as e_ac_db_close: module_logger.error( f"CleanupManager: Error closing AircraftDatabaseManager: {e_ac_db_close}", exc_info=True, ) finally: self.app_controller.aircraft_db_manager = None # Clear reference in AppController module_logger.info("CleanupManager: Cleanup on application exit finished.") def details_window_closed(self, closed_icao24: str): """ Handles the event when a full flight details window is closed. Clears the AppController's references to the closed window if it was the active one. Args: closed_icao24: The ICAO24 of the flight whose details window was closed. """ normalized_closed_icao24 = closed_icao24.lower().strip() # Access active_detail_window_icao and active_detail_window_ref via app_controller if self.app_controller.active_detail_window_icao == normalized_closed_icao24: module_logger.info( f"CleanupManager: Detail window for {normalized_closed_icao24} reported closed. Clearing references in AppController." ) self.app_controller.active_detail_window_ref = None self.app_controller.active_detail_window_icao = None # Also clear MainWindow's convenience reference if it matches (accessed via app_controller) main_window = self.app_controller.main_window if ( main_window and hasattr(main_window, "full_flight_details_window") and main_window.full_flight_details_window and not main_window.full_flight_details_window.winfo_exists() ): # Check if already destroyed by Tkinter main_window.full_flight_details_window = None else: module_logger.debug( f"CleanupManager: A detail window for {normalized_closed_icao24} closed, but it was not the currently tracked active one ({self.app_controller.active_detail_window_icao}). No action on active_detail references." )