#!/usr/bin/python3 import os import sys import signal import threading import logging from http.server import HTTPServer from multiprocessing import Queue # Import our custom modules from alarm_api import AlertApi, run as run_api from alarm_storage import AlarmStorage from alarm_siren import AlarmSiren from ncurses_ui import UI from logging_config import setup_logging # Set up logging logger = setup_logging() class AlarmSystemManager: def __init__(self, api_port: int = 8000, storage_path: str = "data/alerts.json", log_level: int = logging.DEBUG): """ Initialize and manage the entire alarm system Args: api_port (int): Port for the HTTP API server storage_path (str): Path to the JSON storage file log_level (int): Logging level """ # Configure logging logging.getLogger().setLevel(log_level) # Ensure storage directory exists os.makedirs(os.path.dirname(os.path.abspath(storage_path)), exist_ok=True) # Initialize components self.siren = AlarmSiren() self.storage = AlarmStorage(storage_path, siren=self.siren) # API server setup self.api_port = api_port self.api_server = None self.api_thread = None # Synchronization and lifecycle management self.stop_event = threading.Event() # Signal handling setup self._setup_signal_handlers() # Alarm synchronization self._sync_alarms() # UI.. self.ui = UI(self) def _setup_signal_handlers(self): """Set up signal handlers for graceful shutdown""" signal.signal(signal.SIGINT, self._handle_shutdown) signal.signal(signal.SIGTERM, self._handle_shutdown) def _handle_shutdown(self, signum, frame): """ Handle system shutdown signals Args: signum (int): Signal number frame (frame): Current stack frame """ logger.info(f"Received shutdown signal {signum}. Initiating graceful shutdown...") self.stop_event.set() def _sync_alarms(self): """ Synchronize stored alarms with the siren This ensures any saved alarms are loaded and scheduled """ try: saved_alarms = self.storage.get_saved_alerts() logger.info(f"Synchronizing {len(saved_alarms)} saved alarms") for alarm in saved_alarms: if alarm.get('enabled', True): self.siren.schedule_alarm(alarm) except Exception as e: logger.error(f"Error synchronizing alarms: {e}") def _start_api_server(self): """ Start the HTTP API server in a separate thread """ try: def run_server(): server_address = ("", self.api_port) self.api_server = HTTPServer(server_address, AlertApi) logger.info(f"API Server started on port {self.api_port}") # Run until stopped while not self.stop_event.is_set(): self.api_server.handle_request() self.api_server.server_close() logger.info("API Server stopped") # Start API server in a thread self.api_thread = threading.Thread(target=run_server, daemon=True) self.api_thread.start() except Exception as e: logger.error(f"Failed to start API server: {e}") self.stop_event.set() def run(self): """ Main run method to start all system components """ try: # Start API server self._start_api_server() # Start UI ui_thread = self.ui.run() # Log system startup logger.info("Alarm System started successfully") # Wait for shutdown signal while not self.stop_event.is_set(): self.stop_event.wait(timeout=1) # Graceful shutdown logger.info("Initiating system shutdown...") except Exception as e: logger.error(f"System startup error: {e}") finally: self._cleanup() def _cleanup(self): """ Perform cleanup operations during shutdown """ # Close API server if running if self.api_server: try: self.api_server.server_close() except Exception as e: logger.error(f"Error closing API server: {e}") # Stop threads if self.api_thread and self.api_thread.is_alive(): self.api_thread.join(timeout=2) logger.info("Alarm System shutdown complete") def main(): """ Entry point for the Alarm System Parse command-line arguments and start the system """ # Default configurations default_port = 8000 default_storage_path = "data/alerts.json" # Parse port from command line if provided port = int(sys.argv[1]) if len(sys.argv) > 1 else default_port # Create and run the Alarm System try: alarm_system = AlarmSystemManager( api_port=port, storage_path=default_storage_path ) alarm_system.run() except Exception as e: logger.error(f"Fatal error starting Alarm System: {e}", exc_info=True) sys.exit(1) if __name__ == "__main__": main()