import os from datetime import datetime from threading import Event, Thread import psutil import pytz from core.cleanup import CLEANUP_TIMER from core.config import SERVER_OWNER_CALLSIGN from core.constants import SOFTWARE_VERSION from core.data_providers import DATA_PROVIDERS from core.data_store import DATA_STORE from core.prometheus_metrics_handler import alerts_gauge, memory_use_gauge, spots_gauge from telnetserver.telnetserver import TELNET_SERVER from webserver.webserver import WEB_SERVER class StatusReporter: """Provides a timed update of the application's status data.""" def __init__(self, run_interval): """Constructor""" self._run_interval = run_interval self._thread = None self._stop_event = Event() self._startup_time = datetime.now(pytz.UTC) DATA_STORE.status.get()["software_version"] = SOFTWARE_VERSION DATA_STORE.status.get()["server_owner_callsign"] = SERVER_OWNER_CALLSIGN DATA_STORE.status.store() def start(self): """Start the reporter thread""" self._thread = Thread(target=self._run, name="StatusReporter") self._thread.start() def stop(self): """Stop any threads and prepare for application shutdown""" self._stop_event.set() def _run(self): """Thread entry point: report immediately on startup, then on each interval until stopped""" while True: self._report() if self._stop_event.wait(timeout=self._run_interval): break def _report(self): """Write status information""" DATA_STORE.status.get()["uptime"] = (datetime.now(pytz.UTC) - self._startup_time).total_seconds() DATA_STORE.status.get()["mem_use_mb"] = round(psutil.Process(os.getpid()).memory_info().rss / (1024 * 1024), 3) DATA_STORE.status.get()["num_spots"] = len(DATA_STORE.spots.values()) DATA_STORE.status.get()["num_alerts"] = len(DATA_STORE.alerts.values()) DATA_STORE.status.get()["spot_providers"] = [ { "name": p.name, "enabled": p.enabled, "enabled_by_default_in_web_ui": p.enabled_by_default_in_web_ui, "status": p.status, "last_updated": p.last_update_time.replace(tzinfo=pytz.UTC).timestamp() if p.last_update_time.year > 2000 else 0, "last_spot": p.last_spot_time.replace(tzinfo=pytz.UTC).timestamp() if p.last_spot_time.year > 2000 else 0, } for p in DATA_PROVIDERS.spot_providers ] DATA_STORE.status.get()["alert_providers"] = [ { "name": p.name, "enabled": p.enabled, "status": p.status, "last_updated": p.last_update_time.replace(tzinfo=pytz.UTC).timestamp() if p.last_update_time.year > 2000 else 0, } for p in DATA_PROVIDERS.alert_providers ] DATA_STORE.status.get()["solar_condition_providers"] = [ { "name": p.name, "enabled": p.enabled, "status": p.status, "last_updated": p.last_update_time.replace(tzinfo=pytz.UTC).timestamp() if p.last_update_time.year > 2000 else 0, } for p in DATA_PROVIDERS.solar_condition_providers ] DATA_STORE.status.get()["static_data_providers"] = [ { "name": p.name, "enabled": p.enabled, "status": p.status, "last_updated": p.last_update_time.replace(tzinfo=pytz.UTC).timestamp() if p.last_update_time.year > 2000 else 0, } for p in DATA_PROVIDERS.static_data_providers ] DATA_STORE.status.get()["sig_ref_data_providers"] = [ { "sig_name": p.sig_name, "enabled": p.enabled, "status": p.status, "last_updated": p.last_update_time.replace(tzinfo=pytz.UTC).timestamp() if p.last_update_time.year > 2000 else 0, "reference_count": p.reference_count, } for p in DATA_PROVIDERS.sig_ref_data_providers ] DATA_STORE.status.get()["callsign_data_providers"] = [ { "name": p.name, "enabled": p.enabled, "status": p.status, "last_updated": p.last_update_time.replace(tzinfo=pytz.UTC).timestamp() if p.last_update_time.year > 2000 else 0, "lookup_count": p.lookup_count, } for p in DATA_PROVIDERS.callsign_data_providers ] DATA_STORE.status.get()["cleanup"] = { "status": CLEANUP_TIMER.status, "last_ran": CLEANUP_TIMER.last_cleanup_time.replace(tzinfo=pytz.UTC).timestamp() if CLEANUP_TIMER.last_cleanup_time else 0, } DATA_STORE.status.get()["webserver"] = { "status": WEB_SERVER.web_server_metrics.status, "api_requests_per_hour": WEB_SERVER.web_server_metrics.api_requests_per_hour(), "page_requests_per_hour": WEB_SERVER.web_server_metrics.page_requests_per_hour(), "sse_client_count": WEB_SERVER.sse_client_count, } DATA_STORE.status.get()["telnet"] = { "client_count": TELNET_SERVER.client_count, } DATA_STORE.status.store() # Update Prometheus metrics memory_use_gauge.set(psutil.Process(os.getpid()).memory_info().rss) spots_gauge.set(len(DATA_STORE.spots.values())) alerts_gauge.set(len(DATA_STORE.alerts.values()))