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

206 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from enum import StrEnum
class AuthErrorCode(StrEnum):
"""
Stable, machine-readable error codes used across the API.
Rules:
- never rename an existing value (treat as public API)
- only add new values
"""
# Generic / defaults
AUTHENTICATION_ERROR = "AUTHENTICATION_ERROR"
AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED"
FORBIDDEN = "FORBIDDEN"
HTTP_ERROR = "HTTP_ERROR"
INTERNAL_SERVER_ERROR = "INTERNAL_SERVER_ERROR"
# Auth/JWT
INVALID_TOKEN = "INVALID_TOKEN"
SESSION_ALREADY_ACTIVE = "SESSION_ALREADY_ACTIVE"
# Authorization
NOT_STAFF = "NOT_STAFF"
NOT_IN_ACTIVE_PGROUP = "NOT_IN_ACTIVE_PGROUP"
NOT_BATON_HOLDER = "NOT_BATON_HOLDER"
class DAQErrorCode(StrEnum):
BEAMLINE_BUSY = "BEAMLINE_BUSY"
MOUNTING_FAILED = "MOUNTING_FAILED"
UNMOUNTING_FAILED = "UNMOUNTING_FAILED"
LOOP_CENTERING_FAILED = "LOOP_CENTERING_FAILED"
AXC_FAILED = "AXC_FAILED"
TRANSFORMATION_INVALID = "TRANSFORMATION_INVALID"
AUTOMATION_CRITICAL = "AUTOMATION_CRITICAL"
SAMPLE_NOT_FOUND = "SAMPLE_NOT_FOUND"
DATA_COLLECTION_FAILED = "DATA_COLLECTION_FAILED"
RASTER_SCAN_FAILED = "RASTER_SCAN_FAILED"
TELL_WARNING = "TELL_WARNING"
TELL_CRITICAL = "TELL_CRITICAL"
SMARGON_UNAVAILABLE = "SMARGON_UNAVAILABLE"
TELL_UNAVAILABLE = "TELL_UNAVAILABLE"
TELL_CONNECTION_ERROR = "TELL_CONNECTION_ERROR"
TELL_MOUNT_FAILED = "TELL_MOUNT_FAILED"
TELL_BUSY = "TELL_BUSY"
MANUAL_MOUNT_REQUIRED = "MANUAL_MOUNT_REQUIRED"
SMART_MAGNET_FAULT = "SMART_MAGNET_FAULT"
JFJOCH_UNAVAILABLE = "JFJOCH_UNAVAILABLE"
AEROTECH_UNAVAILABLE = "AEROTECH_UNAVAILABLE"
AAREDB_UNAVAILABLE = "AAREDB_UNAVAILABLE"
MAGNET_POSITION_SENSOR_ERROR = "MAGNET_POSITION_SENSOR_ERROR"
_ERROR_CODE_HELP: dict[str, str] = {
# Auth/JWT
AuthErrorCode.AUTHENTICATION_ERROR: (
"Generic authentication problem. Usually means the request lacked valid credentials "
"(expired/invalid token, missing Authorization header, etc.)."
),
AuthErrorCode.AUTHENTICATION_FAILED: (
"Authentication failed during login/token creation. Typically incorrect credentials "
"or an inability to validate the user."
),
AuthErrorCode.INVALID_TOKEN: (
"The provided token could not be decoded/validated (bad signature, expired, malformed). "
"Re-authenticate to obtain a new token."
),
AuthErrorCode.SESSION_ALREADY_ACTIVE: (
"A different session currently owns control. Use “force current session” (if allowed) "
"or wait for the active session to expire/end."
),
AuthErrorCode.NOT_BATON_HOLDER: (
"A different session currently owns the baton. Request the baton or wait for active session to expire/end"),
# Authorization
AuthErrorCode.FORBIDDEN: (
"Generic permissions failure. The user is authenticated but not allowed to perform this action."
),
AuthErrorCode.NOT_STAFF: (
"This action requires staff privileges. Log in with a staff account or ask staff to perform it."
),
AuthErrorCode.NOT_IN_ACTIVE_PGROUP: (
"You are not a member of the currently active p-group. Change p-group or use an account "
"that belongs to the active group."
),
# Generic
AuthErrorCode.HTTP_ERROR: (
"Generic HTTP error wrapper. The server returned an HTTPException that wasnt mapped to a more specific code."
),
AuthErrorCode.INTERNAL_SERVER_ERROR: (
"Unhandled server error. Check server logs for a stack trace and context."
),
DAQErrorCode.BEAMLINE_BUSY: (
"Beamline state is set to Busy by prior action. If this state persists an additional error may have occurred, "
"preventing the state from being released, this should timeout within 10 minutes."
"If this occurs please seek assistance from your local contact."
),
DAQErrorCode.MOUNTING_FAILED: (
"The requested sample could not be mounted. Check TELL/robot state, sample location, and hardware readiness."
),
DAQErrorCode.UNMOUNTING_FAILED: (
"The mounted sample could not be unmounted. Check robot state and whether the sample changer is ready."
),
DAQErrorCode.LOOP_CENTERING_FAILED: (
"Automatic loop centering failed to locate or position the sample reliably."
),
DAQErrorCode.AXC_FAILED: (
"Automatic X-ray centering failed. Verify sample visibility, alignment, and detector/beam conditions."
),
DAQErrorCode.TRANSFORMATION_INVALID: (
"The requested beamline state transition is not allowed from the current state."
),
DAQErrorCode.AUTOMATION_CRITICAL: (
"A critical failure occurred during fully automated measurement. The GUI should stop "
"dispatching further queue items and staff/operator recovery may be required."
),
DAQErrorCode.SAMPLE_NOT_FOUND: (
"The requested sample could not be found in the available sample lists."
),
DAQErrorCode.DATA_COLLECTION_FAILED: (
"Rotation/data collection failed before a valid result was produced."
),
DAQErrorCode.RASTER_SCAN_FAILED: (
"Raster scan failed before a valid result was produced."
),
DAQErrorCode.TELL_WARNING: (
"TELL reported a warning condition. The request may not have completed cleanly."
),
DAQErrorCode.TELL_CRITICAL: (
"TELL reported a critical error condition requiring operator attention."
),
DAQErrorCode.SMARGON_UNAVAILABLE: (
"Smargon communication failed or the controller is unavailable."
),
DAQErrorCode.TELL_UNAVAILABLE: (
"TELL communication failed or the service is unavailable."
),
DAQErrorCode.TELL_CONNECTION_ERROR: (
"The server could not establish or maintain a connection to TELL."
),
DAQErrorCode.TELL_MOUNT_FAILED: (
"TELL reported that the mount/unmount operation failed."
),
DAQErrorCode.TELL_BUSY: (
"A TELL command was requested while TELL was still busy processing another action."
),
DAQErrorCode.MANUAL_MOUNT_REQUIRED: (
"Automatic mounting could not proceed and manual intervention is required."
),
DAQErrorCode.SMART_MAGNET_FAULT: (
"The smart magnet system reported a fault or unsafe condition."
),
DAQErrorCode.JFJOCH_UNAVAILABLE: (
"JFJoch detector communication failed or the detector service is unavailable."
),
DAQErrorCode.AEROTECH_UNAVAILABLE: (
"Aerotech communication failed or the motion controller is unavailable."
),
DAQErrorCode.AAREDB_UNAVAILABLE: (
"AareDB communication failed or the database service is unavailable."
),
DAQErrorCode.MAGNET_POSITION_SENSOR_ERROR: (
"The magnet position sensor reported an invalid or unsafe state."
),
}
def error_code_help(code: str) -> str | None:
"""
Return a human help message for a code string, if known.
Accepts either enum value strings or raw strings.
"""
if not code:
return None
return _ERROR_CODE_HELP.get(str(code))
def export_error_code_help() -> dict[str, str]:
"""
Export help text as {"CODE": "help text", ...}
"""
return {str(k): str(v) for k, v in _ERROR_CODE_HELP.items()}
def export_error_codes_grouped() -> dict[str, dict[str, str]]:
"""
Export codes grouped by enum class name:
{
"AuthErrorCode": {"INVALID_TOKEN": "INVALID_TOKEN", ...},
"DAQErrorCode": {"BEAMLINE_BUSY": "BEAMLINE_BUSY", ...}
}
"""
enums: tuple[type[StrEnum], ...] = (AuthErrorCode, DAQErrorCode)
return {e.__name__: {c.name: str(c.value) for c in e} for e in enums}
def export_error_codes() -> dict[str, str]:
"""
Backwards-compatible, flat export used by older clients/tests/docs:
{"INVALID_TOKEN": "INVALID_TOKEN", ...}
NOTE: This intentionally exports only AuthErrorCode to avoid breaking
existing consumers that assume a flat map and/or specific keys.
"""
return {c.name: str(c.value) for c in AuthErrorCode}