102 lines
3.8 KiB
Python
102 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).
|