Files
AareDAQ/src/aare/common/exception_handler.py
T

312 lines
9.1 KiB
Python

from __future__ import annotations
import time
from aare.common.logger_config import setup_logger
from aare.common.error_codes import AuthErrorCode, DAQErrorCode
logger = setup_logger("aareDAQ")
class TransformationInvalidException(Exception):
def __init__(self, message: str = "Transformation is not implemented"):
super().__init__(message)
self.message = message
logger.error(message, extra={"exception:": Exception})
def __str__(self) -> str:
return self.message
class LoopCenteringFailed(Exception):
def __init__(self, message: str = "Loop Centering did not detect a sample"):
super().__init__(message)
self.message = message
logger.error(message, extra={"exception:": Exception})
def __str__(self) -> str:
return self.message
class MountingFailed(Exception):
def __init__(self, message: str = "A sample was not mounted"):
super().__init__(message)
self.message = message
logger.error(message, extra={"exception:": Exception})
def __str__(self) -> str:
return self.message
class WarningTellException(Exception):
def __init__(self, message: str = "Warning error in TELL"):
super().__init__(message)
self.message = message
logger.error(message, extra={"exception:": Exception})
def __str__(self) -> str:
return self.message
class CriticalTellException(Exception):
def __init__(self, message: str = "Critical error in TELL"):
super().__init__(message)
self.message = message
logger.error(message, extra={"exception:": Exception})
def __str__(self) -> str:
return self.message
class AXCFailed(Exception):
def __init__(self, message: str = "Auto X-ray centering failed"):
super().__init__(message)
self.message = message
logger.error(message, extra={"exception:": Exception})
def __str__(self) -> str:
return self.message
class BeamlineBusyException(Exception):
def __init__(self, message: str = "Beamline is in busy state"):
super().__init__(message)
self.message = message
logger.error(message, extra={"exception:": Exception})
def __str__(self) -> str:
return self.message
class SampleException(Exception):
def __init__(self, message: str = "Sample not found"):
super().__init__(message)
self.message = message
logger.error(message, extra={"exception:": Exception})
def __str__(self) -> str:
return self.message
class AuthenticationException(Exception):
def __init__(self,
message: str = "Authentication failed.",
*,
status_code: int = 401,
headers: dict[str, str] | None = None,
code: AuthErrorCode = AuthErrorCode.AUTHENTICATION_FAILED):
super().__init__(message)
self.message = message
self.status_code = status_code
self.headers = headers
self.code = code
logger.error(message, extra={"exception:": Exception})
def __str__(self) -> str:
return self.message
class UserRightsException(Exception):
_last_log_ts_by_message: dict[str, float] = {}
_throttle_window_s = 30.0
def __init__(self,
message: str = "User does not have rights to perform this action.",
*,
status_code: int = 403,
headers: dict[str, str] | None = None,
code: AuthErrorCode = AuthErrorCode.FORBIDDEN):
super().__init__(message)
self.message = message
self.status_code = status_code
self.headers = headers
self.code = code
now = time.monotonic()
last_ts = self._last_log_ts_by_message.get(message, 0.0)
if (now - last_ts) >= self._throttle_window_s:
self._last_log_ts_by_message[message] = now
logger.warning(message, extra={"exception:": Exception})
def __str__(self) -> str:
return self.message
class SmargonCommunicationError(Exception):
"""
Raised when Smargon HTTP communication fails (connection refused, timeout, bad HTTP status, etc).
Keep the original exception in `__cause__` by using `raise ... from e`.
"""
def __init__(
self,
message: str = "Smargon communication error",
*,
endpoint: str | None = None,
base_url: str | None = None,
operation: str | None = None, # e.g. "GET" / "PUT"
status_code: int | None = None,
):
super().__init__(message)
self.message = message
self.endpoint = endpoint
self.base_url = base_url
self.operation = operation
self.status_code = status_code
logger.error(
message,
extra={
"device": "smargon",
"operation": operation,
"endpoint": endpoint,
"base_url": base_url,
"status_code": status_code,
},
)
def __str__(self) -> str:
return self.message
class TellCommunicationError(Exception):
"""
Raised when TELL HTTP/PShell communication fails (timeouts, connection refused, etc).
Intended to be caught centrally by FastAPI exception handlers.
"""
def __init__(
self,
message: str = "TELL communication error",
*,
endpoint: str | None = None,
base_url: str | None = None,
operation: str | None = None, # e.g. "GET"
):
super().__init__(message)
self.message = message
self.endpoint = endpoint
self.base_url = base_url
self.operation = operation
logger.error(
message,
extra={
"device": "tell",
"operation": operation,
"endpoint": endpoint,
"base_url": base_url,
},
)
def __str__(self) -> str:
return self.message
class JFJochCommunicationError(Exception):
"""
Raised when JFJoch HTTP/API communication fails.
Intended for scan-time fallbacks and GUI-visible alerts.
"""
def __init__(
self,
message: str = "JFJoch communication error",
*,
operation: str | None = None,
endpoint: str | None = None,
base_url: str | None = None,
status_code: int | None = None,
):
super().__init__(message)
self.message = message
self.operation = operation
self.endpoint = endpoint
self.base_url = base_url
self.status_code = status_code
logger.error(
message,
extra={
"device": "jfjjoch",
"operation": operation,
"endpoint": endpoint,
"base_url": base_url,
"status_code": status_code,
},
)
def __str__(self) -> str:
return self.message
class AareDBCommunicationError(Exception):
"""Raised when AareDB HTTPS communication fails."""
def __init__(
self,
message: str = "AareDB communication error",
*,
operation: str | None = None,
endpoint: str | None = None,
base_url: str | None = None,
status_code: int | None = None,
):
super().__init__(message)
self.message = message
self.operation = operation
self.endpoint = endpoint
self.base_url = base_url
self.status_code = status_code
logger.error(
message,
extra={
"device": "aaredb",
"operation": operation,
"endpoint": endpoint,
"base_url": base_url,
"status_code": status_code,
},
)
def __str__(self) -> str:
return self.message
class AerotechCommunicationError(Exception):
"""
Raised when Aerotech HTTP/API communication fails (connection refused, timeout, bad HTTP status, etc).
Keep the original exception in `__cause__` by using `raise ... from e`.
"""
def __init__(
self,
message: str = "Aerotech communication error",
*,
endpoint: str | None = None,
base_url: str | None = None,
operation: str | None = None,
status_code: int | None = None,
):
super().__init__(message)
self.message = message
self.endpoint = endpoint
self.base_url = base_url
self.operation = operation
self.status_code = status_code
logger.error(
message,
extra={
"device": "aerotech",
"operation": operation,
"endpoint": endpoint,
"base_url": base_url,
"status_code": status_code,
},
)
def __str__(self) -> str:
return self.message
class MagnetPositionSensorErorr(Exception):
def __init__(self, message: str = "Magnet position sensor error"):
super().__init__(message)
self.message = message
logger.error(
message,
extra={"device": "magnet_position_sensor"},
)
def __str__(self) -> str:
return self.message