#!/usr/bin/env python3 """ Calejo Control Adapter - Phase 1 Implementation Core infrastructure implementation including: - Database setup and connection pooling - Auto-discovery of pump stations and pumps - Configuration management - Structured logging and audit system """ import asyncio import signal import sys from src.database.client import DatabaseClient from src.core.auto_discovery import AutoDiscovery from src.core.safety import SafetyLimitEnforcer from src.core.emergency_stop import EmergencyStopManager from src.core.optimization_manager import OptimizationPlanManager from src.core.logging import setup_logging, AuditLogger from config.settings import settings class CalejoControlAdapterPhase1: """Main application class for Phase 1 implementation.""" def __init__(self): # Setup logging first self.logger = setup_logging() # Initialize core components self.db_client = DatabaseClient( database_url=settings.database_url, min_connections=settings.db_min_connections, max_connections=settings.db_max_connections ) self.auto_discovery = AutoDiscovery( db_client=self.db_client, refresh_interval_minutes=settings.auto_discovery_refresh_minutes ) self.emergency_stop_manager = EmergencyStopManager(self.db_client) self.safety_enforcer = SafetyLimitEnforcer(self.db_client, self.emergency_stop_manager) self.optimization_manager = OptimizationPlanManager( db_client=self.db_client, refresh_interval_seconds=settings.optimization_refresh_seconds ) self.audit_logger = AuditLogger(self.db_client) self.running = False self.startup_time = None async def start(self): """Start the Phase 1 application.""" self.startup_time = asyncio.get_event_loop().time() self.logger.info( "phase1_application_starting", app_name=settings.app_name, version=settings.app_version, environment=settings.environment ) try: # Connect to database await self.db_client.connect() # Initialize auto-discovery if settings.auto_discovery_enabled: await self.auto_discovery.discover() # Validate discovery validation = self.auto_discovery.validate_discovery() if not validation['valid']: self.logger.warning( "auto_discovery_validation_issues", issues=validation['issues'] ) # Start periodic discovery asyncio.create_task(self.auto_discovery.start_periodic_discovery()) # Initialize safety framework await self.safety_enforcer.load_safety_limits() # Initialize optimization plan monitoring if settings.optimization_monitoring_enabled: await self.optimization_manager.start_monitoring() # Log startup to audit trail self.audit_logger.log( event_type='SYSTEM_STARTUP', severity='INFO', user_id='system', action='startup', resource='system', result='SUCCESS', event_data={ 'version': settings.app_version, 'phase': '1', 'components_initialized': ['database', 'auto_discovery', 'safety', 'optimization'] } ) self.running = True startup_duration = asyncio.get_event_loop().time() - self.startup_time self.logger.info( "phase1_application_started", startup_duration_seconds=round(startup_duration, 2), station_count=len(self.auto_discovery.get_stations()), pump_count=len(self.auto_discovery.get_pumps()), safety_limits_loaded=len(self.safety_enforcer.safety_limits_cache), optimization_plans_loaded=len(self.optimization_manager.get_all_pump_plans()) ) # Print status information self.print_status() # Keep application running while self.running: await asyncio.sleep(1) except Exception as e: self.logger.error("phase1_application_startup_failed", error=str(e)) await self.stop() raise async def stop(self): """Stop the application.""" self.logger.info("phase1_application_stopping") self.running = False # Log shutdown to audit trail self.audit_logger.log( event_type='SYSTEM_SHUTDOWN', severity='INFO', user_id='system', action='shutdown', resource='system', result='SUCCESS' ) # Stop optimization monitoring await self.optimization_manager.stop_monitoring() # Disconnect from database await self.db_client.disconnect() self.logger.info("phase1_application_stopped") def get_status(self) -> dict: """Get application status information.""" return { "running": self.running, "app_name": settings.app_name, "version": settings.app_version, "environment": settings.environment, "database": self.db_client.get_connection_stats(), "auto_discovery": self.auto_discovery.get_discovery_status(), "safety_limits_loaded": len(self.safety_enforcer.safety_limits_cache), "optimization_manager": self.optimization_manager.get_status() } def print_status(self): """Print human-readable status information.""" status = self.get_status() print("\n" + "="*60) print(f"Calejo Control Adapter - Phase 1 Status") print("="*60) print(f"Application: {status['app_name']} v{status['version']}") print(f"Environment: {status['environment']}") print(f"Status: {'RUNNING' if status['running'] else 'STOPPED'}") print() # Database status db_stats = status['database'] print(f"Database:") print(f" Status: {db_stats.get('pool_status', 'N/A')}") print(f" Connections: {db_stats.get('min_connections', 'N/A')}-{db_stats.get('max_connections', 'N/A')}") # Auto-discovery status discovery_status = status['auto_discovery'] print(f"\nAuto-Discovery:") print(f" Stations: {discovery_status['station_count']}") print(f" Pumps: {discovery_status['pump_count']}") print(f" Last Discovery: {discovery_status['last_discovery'] or 'Never'}") # Safety framework status print(f"\nSafety Framework:") print(f" Limits Loaded: {status['safety_limits_loaded']}") # Optimization manager status opt_status = status['optimization_manager'] print(f"\nOptimization Manager:") print(f" Status: {'RUNNING' if opt_status['running'] else 'STOPPED'}") print(f" Pump Plans: {opt_status['active_pump_plans']}") print(f" Generic Plans: {opt_status['active_generic_plans']}") print(f" Last Refresh: {opt_status['last_refresh'] or 'Never'}") print("="*60) print("\nPress Ctrl+C to stop the application\n") async def main(): """Main application entry point for Phase 1.""" app = CalejoControlAdapterPhase1() # Setup signal handlers for graceful shutdown def signal_handler(signum, frame): logger = app.logger logger.info("signal_received", signal=signum) asyncio.create_task(app.stop()) signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) try: await app.start() except KeyboardInterrupt: await app.stop() except Exception as e: logger = app.logger logger.error("phase1_application_failed", error=str(e)) sys.exit(1) if __name__ == "__main__": asyncio.run(main())