import logging import threading import time from core.config import config, create_provider_from_config logger = logging.getLogger(__name__) class DataProviders: """Global object for storing data providers.""" def __init__(self): self.spot_providers = [] self.alert_providers = [] self.solar_condition_providers = [] self.static_data_providers = [] self.sig_ref_data_providers = [] self.callsign_data_providers = [] self._startup_timers = [] def setup(self): for entry in config["spot_providers"]: self.spot_providers.append(create_provider_from_config("providers.spot", entry)) for entry in config["alert_providers"]: self.alert_providers.append(create_provider_from_config("providers.alert", entry)) for entry in config.get("solar_condition_providers", []): self.solar_condition_providers.append(create_provider_from_config("providers.solarconditions", entry)) for entry in config.get("static_data_providers", []): self.static_data_providers.append(create_provider_from_config("providers.staticdata", entry)) for entry in config.get("sig_ref_data_providers", []): self.sig_ref_data_providers.append(create_provider_from_config("providers.sigrefdata", entry)) for entry in config.get("callsign_data_providers", []): self.callsign_data_providers.append(create_provider_from_config("providers.callsigndata", entry)) @staticmethod def start_providers(providers, provider_type): """Helper method to activate enabled providers in the list.""" logger.info(f"Starting {provider_type} providers...") for p in providers: if p.enabled: p.start() def start(self): # Start data providers before spot/alert providers so the lookup data is there already for incoming spots. # Each category is fired off after a small delay to give the rest of Spothole chance to start up. self._startup_timers = [ threading.Timer(5.0, lambda: self.start_providers(self.static_data_providers, "static data")), threading.Timer(10.0, lambda: self.start_providers(self.callsign_data_providers, "callsign data")), threading.Timer(15.0, lambda: self.start_providers(self.spot_providers, "spot")), threading.Timer(20.0, lambda: self.start_providers(self.alert_providers, "alert")), threading.Timer( 25.0, lambda: self.start_providers(self.solar_condition_providers, "solar condition"), ), threading.Timer(30.0, lambda: self.start_providers(self.sig_ref_data_providers, "SIG ref data")), ] for t in self._startup_timers: t.daemon = True t.start() def stop(self): # Cancel any startup timers that haven't fired yet for t in self._startup_timers: t.cancel() # Stop all providers all_providers = [ p for p in ( self.spot_providers + self.alert_providers + self.solar_condition_providers + self.sig_ref_data_providers + self.static_data_providers + self.callsign_data_providers ) if p.enabled ] if not all_providers: return def stop_provider(p): try: p.stop() except Exception: logger.exception("Exception stopping provider") threads = [threading.Thread(target=stop_provider, args=(p,), daemon=True) for p in all_providers] for t in threads: t.start() deadline = time.monotonic() + 40 for t in threads: t.join(timeout=max(0.0, deadline - time.monotonic())) still_running = [t for t in threads if t.is_alive()] if still_running: logger.warning("Some threads did not stop in time!") # Global object DATA_PROVIDERS = DataProviders()