CalejoControl/src/core/emergency_stop.py

327 lines
11 KiB
Python
Raw Normal View History

"""
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.client import DatabaseClient
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: DatabaseClient):
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))