CalejoControl/src/main_phase1.py

233 lines
8.4 KiB
Python

#!/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.flexible_client import FlexibleDatabaseClient
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 = FlexibleDatabaseClient(
database_url=settings.database_url,
pool_size=settings.db_pool_size,
max_overflow=settings.db_max_overflow,
pool_timeout=settings.db_pool_timeout,
pool_recycle=settings.db_pool_recycle
)
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())