""" Emergency Stop Manager for Calejo Control Adapter. Implements system-wide and targeted emergency stop functionality with manual clearance and audit trail. """ from typing import Dict, List, Optional, Set, Any from datetime import datetime import structlog from src.database.flexible_client import FlexibleDatabaseClient logger = structlog.get_logger() class EmergencyStopManager: """ Manages emergency stop functionality for pumps and stations. Features: - Single pump emergency stop - Station-wide emergency stop - System-wide emergency stop - Manual clearance with audit trail - Integration with all protocol interfaces """ def __init__(self, db_client: FlexibleDatabaseClient): self.db_client = db_client self.emergency_stop_pumps: Set[tuple] = set() # (station_id, pump_id) self.emergency_stop_stations: Set[str] = set() self.system_emergency_stop = False self.emergency_stop_history: List[Dict] = [] def emergency_stop_pump(self, station_id: str, pump_id: str, reason: str = "Manual stop", user_id: str = "system") -> bool: """ Emergency stop a specific pump. Args: station_id: Station identifier pump_id: Pump identifier reason: Reason for emergency stop user_id: User who initiated the stop Returns: True if stop was successful """ try: key = (station_id, pump_id) self.emergency_stop_pumps.add(key) # Record emergency stop event self._record_emergency_stop_event(station_id, pump_id, 'PUMP', reason, user_id) logger.critical( "emergency_stop_pump_activated", station_id=station_id, pump_id=pump_id, reason=reason, user_id=user_id ) return True except Exception as e: logger.error( "emergency_stop_pump_failed", station_id=station_id, pump_id=pump_id, error=str(e) ) return False def emergency_stop_station(self, station_id: str, reason: str = "Manual stop", user_id: str = "system") -> bool: """ Emergency stop all pumps in a station. Args: station_id: Station identifier reason: Reason for emergency stop user_id: User who initiated the stop Returns: True if stop was successful """ try: self.emergency_stop_stations.add(station_id) # Record emergency stop event self._record_emergency_stop_event(station_id, None, 'STATION', reason, user_id) logger.critical( "emergency_stop_station_activated", station_id=station_id, reason=reason, user_id=user_id ) return True except Exception as e: logger.error( "emergency_stop_station_failed", station_id=station_id, error=str(e) ) return False def emergency_stop_system(self, reason: str = "Manual stop", user_id: str = "system") -> bool: """ Emergency stop all pumps in the system. Args: reason: Reason for emergency stop user_id: User who initiated the stop Returns: True if stop was successful """ try: self.system_emergency_stop = True # Record emergency stop event self._record_emergency_stop_event(None, None, 'SYSTEM', reason, user_id) logger.critical( "emergency_stop_system_activated", reason=reason, user_id=user_id ) return True except Exception as e: logger.error("emergency_stop_system_failed", error=str(e)) return False def clear_emergency_stop_pump(self, station_id: str, pump_id: str, reason: str = "Manual clearance", user_id: str = "system") -> bool: """ Clear emergency stop for a specific pump. Args: station_id: Station identifier pump_id: Pump identifier reason: Reason for clearance user_id: User who cleared the stop Returns: True if clearance was successful """ try: key = (station_id, pump_id) if key in self.emergency_stop_pumps: self.emergency_stop_pumps.remove(key) # Record clearance event self._record_emergency_stop_clearance(station_id, pump_id, 'PUMP', reason, user_id) logger.info( "emergency_stop_pump_cleared", station_id=station_id, pump_id=pump_id, reason=reason, user_id=user_id ) return True else: logger.warning( "emergency_stop_pump_not_active", station_id=station_id, pump_id=pump_id ) return False except Exception as e: logger.error( "emergency_stop_pump_clearance_failed", station_id=station_id, pump_id=pump_id, error=str(e) ) return False def clear_emergency_stop_station(self, station_id: str, reason: str = "Manual clearance", user_id: str = "system") -> bool: """ Clear emergency stop for all pumps in a station. Args: station_id: Station identifier reason: Reason for clearance user_id: User who cleared the stop Returns: True if clearance was successful """ try: if station_id in self.emergency_stop_stations: self.emergency_stop_stations.remove(station_id) # Record clearance event self._record_emergency_stop_clearance(station_id, None, 'STATION', reason, user_id) logger.info( "emergency_stop_station_cleared", station_id=station_id, reason=reason, user_id=user_id ) return True else: logger.warning( "emergency_stop_station_not_active", station_id=station_id ) return False except Exception as e: logger.error( "emergency_stop_station_clearance_failed", station_id=station_id, error=str(e) ) return False def clear_emergency_stop_system(self, reason: str = "Manual clearance", user_id: str = "system") -> bool: """ Clear system-wide emergency stop. Args: reason: Reason for clearance user_id: User who cleared the stop Returns: True if clearance was successful """ try: if self.system_emergency_stop: self.system_emergency_stop = False # Record clearance event self._record_emergency_stop_clearance(None, None, 'SYSTEM', reason, user_id) logger.info( "emergency_stop_system_cleared", reason=reason, user_id=user_id ) return True else: logger.warning("emergency_stop_system_not_active") return False except Exception as e: logger.error("emergency_stop_system_clearance_failed", error=str(e)) return False def is_emergency_stop_active(self, station_id: str, pump_id: str) -> bool: """ Check if emergency stop is active for a pump. Args: station_id: Station identifier pump_id: Pump identifier Returns: True if emergency stop is active """ # Check system-wide stop if self.system_emergency_stop: return True # Check station-wide stop if station_id in self.emergency_stop_stations: return True # Check pump-specific stop key = (station_id, pump_id) if key in self.emergency_stop_pumps: return True return False def get_emergency_stop_status(self) -> Dict[str, Any]: """Get current emergency stop status.""" return { 'system_emergency_stop': self.system_emergency_stop, 'emergency_stop_stations': list(self.emergency_stop_stations), 'emergency_stop_pumps': [ {'station_id': station_id, 'pump_id': pump_id} for station_id, pump_id in self.emergency_stop_pumps ], 'total_active_stops': ( (1 if self.system_emergency_stop else 0) + len(self.emergency_stop_stations) + len(self.emergency_stop_pumps) ) } def _record_emergency_stop_event(self, station_id: Optional[str], pump_id: Optional[str], stop_type: str, reason: str, user_id: str): """Record emergency stop event in database.""" try: query = """ INSERT INTO emergency_stop_events (station_id, pump_id, stop_type, reason, user_id, timestamp) VALUES (%s, %s, %s, %s, %s, NOW()) """ self.db_client.execute(query, (station_id, pump_id, stop_type, reason, user_id)) except Exception as e: logger.error("failed_to_record_emergency_stop_event", error=str(e)) def _record_emergency_stop_clearance(self, station_id: Optional[str], pump_id: Optional[str], stop_type: str, reason: str, user_id: str): """Record emergency stop clearance event in database.""" try: query = """ INSERT INTO emergency_stop_events (station_id, pump_id, stop_type, event_type, reason, user_id, timestamp) VALUES (%s, %s, %s, 'CLEARED', %s, %s, NOW()) """ self.db_client.execute(query, (station_id, pump_id, stop_type, reason, user_id)) except Exception as e: logger.error("failed_to_record_emergency_stop_clearance", error=str(e))