190 lines
5.6 KiB
Python
Executable File
190 lines
5.6 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
import os
|
|
import sys
|
|
import signal
|
|
import threading
|
|
import logging
|
|
from http.server import HTTPServer
|
|
from multiprocessing import Queue
|
|
import subprocess
|
|
|
|
# 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, control_queue=self.siren.control_queue)
|
|
|
|
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)
|
|
|
|
# Kill any remaining mpg123 processes
|
|
try:
|
|
subprocess.run(['pkill', 'mpg123'], check=False)
|
|
except Exception as e:
|
|
logger.error(f"Error killing mpg123 processes: {e}")
|
|
|
|
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()
|