SXXXXXXX_FlightMonitor/flightmonitor/data/base_live_data_adapter.py

91 lines
3.8 KiB
Python

# FlightMonitor/data/base_live_data_adapter.py
"""
Defines the abstract base class for all live data adapters.
All live data adapters (e.g., OpenSky, ADS-B Exchange) should inherit from this class
to provide a consistent interface for the AppController.
"""
import abc # Import for Abstract Base Classes
import threading # All adapters will run in a separate thread
from queue import Queue # For the output queue, common to all adapters
from typing import Dict, Any, Optional # Type hints for clarity
try:
from flightmonitor.utils.logger import get_logger
logger = get_logger(__name__)
except ImportError:
import logging
logger = logging.getLogger(__name__)
if not logger.hasHandlers():
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s [%(levelname)s] %(funcName)s (%(threadName)s): %(message)s",
)
logger.warning("AircraftDatabaseManager using fallback standard Python logger.")
# Define the common message format for adapter output
# This ensures consistency for the LiveDataProcessor
AdapterMessage = Dict[str, Any]
class BaseLiveDataAdapter(abc.ABC, threading.Thread):
"""
Abstract Base Class for live flight data adapters.
All concrete adapter implementations (e.g., OpenSkyLiveAdapter, ADSBExchangeLiveAdapter)
must inherit from this class and implement its abstract methods.
It combines the properties of an Abstract Base Class with those of a Thread,
as all our adapters are expected to run in separate threads.
"""
def __init__(self, output_queue: Queue[AdapterMessage], bounding_box: Dict[str, float], polling_interval: float, daemon: bool = True, name: Optional[str] = None):
"""
Initializes the base live data adapter.
Args:
output_queue (Queue[AdapterMessage]): The queue to which processed data (or status) will be sent.
bounding_box (Dict[str, float]): The geographical bounding box for data fetching.
polling_interval (float): The interval in seconds between data fetches.
daemon (bool): Whether the thread should be a daemon thread.
name (Optional[str]): The name of the thread.
"""
# Call the constructor of the threading.Thread base class
super().__init__(daemon=daemon, name=name)
# Common attributes for all adapters
self.output_queue = output_queue
self.bounding_box = bounding_box
self.polling_interval = polling_interval
# Event to signal the thread to stop its execution loop
self._stop_event = threading.Event()
@abc.abstractmethod
def run(self):
"""
The main execution method for the adapter thread.
Concrete subclasses must implement this to contain the data fetching and processing loop.
"""
# This method is abstract and must be implemented by subclasses.
# It typically contains a while loop checking self._stop_event.is_set().
pass
def stop(self):
"""
Signals the adapter thread to stop its execution.
This method is thread-safe and sets the internal stop event.
"""
self._stop_event.set()
logger.debug(f"{self.name}: Stop signal set.") # Use module_logger if available, otherwise print
def is_stopped(self) -> bool:
"""
Checks if the adapter thread has been signaled to stop.
Returns:
bool: True if the stop event has been set, False otherwise.
"""
return self._stop_event.is_set()
# It's generally good practice to define a common _send_status_to_queue method
# here if its structure is identical across all adapters, but for now, it remains
# in the concrete adapters as its details might vary slightly (e.g., provider name).