CalejoControl/src/core/emergency_stop.py

327 lines
11 KiB
Python

"""
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))