From e5ffbb9b188ab8ca7ae02fafee22603a8efa7272 Mon Sep 17 00:00:00 2001 From: appleb_m Date: Thu, 23 Apr 2026 17:22:36 +0200 Subject: [PATCH] Server: updated server_exception_handler.py and also error_codes.py to cover all custom exceptions --- src/aare/common/error_codes.py | 86 +++++++++++++++++++- src/aare/daq/server_exception_handler.py | 99 ++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 1 deletion(-) diff --git a/src/aare/common/error_codes.py b/src/aare/common/error_codes.py index 9179291b..d67bf298 100644 --- a/src/aare/common/error_codes.py +++ b/src/aare/common/error_codes.py @@ -30,6 +30,27 @@ class AuthErrorCode(StrEnum): 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" + 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 @@ -73,7 +94,70 @@ _ERROR_CODE_HELP: dict[str, str] = { "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.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: diff --git a/src/aare/daq/server_exception_handler.py b/src/aare/daq/server_exception_handler.py index 7b4307df..e02555fc 100644 --- a/src/aare/daq/server_exception_handler.py +++ b/src/aare/daq/server_exception_handler.py @@ -21,6 +21,17 @@ from aare.common.exception_handler import ( TellCommunicationError, JFJochCommunicationError, AerotechCommunicationError, + DataCollectionException, + RasterScanException, + UnmountingFailed, + AXCFailed, + AareDBCommunicationError, + MagnetPositionSensorErorr, + ManualMountException, + SmartMagnetFaultException, + TellMountFailedException, + TellCommandWhileBusyException, + TellConnectionException, ) logger = setup_logger("aareDAQ") @@ -56,6 +67,13 @@ def register_exception_handlers(app) -> None: content=_error_payload(code="MOUNTING_FAILED", message=str(exc)), ) + @app.exception_handler(UnmountingFailed) + async def unmounting_failed_handler(request: Request, exc: UnmountingFailed) -> JSONResponse: + return JSONResponse( + status_code=api_status.HTTP_500_INTERNAL_SERVER_ERROR, + content=_error_payload(code="UNMOUNTING_FAILED", message=str(exc) or "Failed to unmount sample"), + ) + @app.exception_handler(WarningTellException) async def warning_tell_handler(request: Request, exc: WarningTellException) -> JSONResponse: return JSONResponse( @@ -77,6 +95,13 @@ def register_exception_handlers(app) -> None: content=_error_payload(code="LOOP_CENTERING_FAILED", message=str(exc)), ) + @app.exception_handler(AXCFailed) + async def axc_failed_handler(request: Request, exc: AXCFailed) -> JSONResponse: + return JSONResponse( + status_code=api_status.HTTP_500_INTERNAL_SERVER_ERROR, + content=_error_payload(code="AXC_FAILED", message=str(exc) or "Auto X-ray centering failed"), + ) + @app.exception_handler(TransformationInvalidException) async def transformation_invalid_handler(request: Request, exc: TransformationInvalidException) -> JSONResponse: return JSONResponse( @@ -91,6 +116,20 @@ def register_exception_handlers(app) -> None: content=_error_payload(code="BEAMLINE_BUSY", message=str(exc) or "Beamline is busy"), ) + @app.exception_handler(DataCollectionException) + async def data_collection_failed_handler(request: Request, exc: DataCollectionException) -> JSONResponse: + return JSONResponse( + status_code=api_status.HTTP_500_INTERNAL_SERVER_ERROR, + content=_error_payload(code="DATA_COLLECTION_FAILED", message=str(exc) or "Data collection failed"), + ) + + @app.exception_handler(RasterScanException) + async def raster_scan_failed_handler(request: Request, exc: RasterScanException) -> JSONResponse: + return JSONResponse( + status_code=api_status.HTTP_500_INTERNAL_SERVER_ERROR, + content=_error_payload(code="RASTER_SCAN_FAILED", message=str(exc) or "Raster scan failed"), + ) + @app.exception_handler(AuthenticationException) async def authentication_exception_handler(request: Request, exc: AuthenticationException) -> JSONResponse: return JSONResponse( @@ -120,6 +159,41 @@ def register_exception_handlers(app) -> None: content=_error_payload(code="SAMPLE_NOT_FOUND", message=str(exc) or "Sample not found"), ) + @app.exception_handler(ManualMountException) + async def manual_mount_handler(request: Request, exc: ManualMountException) -> JSONResponse: + return JSONResponse( + status_code=api_status.HTTP_409_CONFLICT, + content=_error_payload(code="MANUAL_MOUNT_REQUIRED", message=str(exc) or "Manual mount intervention required"), + ) + + @app.exception_handler(SmartMagnetFaultException) + async def smart_magnet_fault_handler(request: Request, exc: SmartMagnetFaultException) -> JSONResponse: + return JSONResponse( + status_code=api_status.HTTP_503_SERVICE_UNAVAILABLE, + content=_error_payload(code="SMART_MAGNET_FAULT", message=str(exc) or "Smart magnet fault"), + ) + + @app.exception_handler(TellMountFailedException) + async def tell_mount_failed_handler(request: Request, exc: TellMountFailedException) -> JSONResponse: + return JSONResponse( + status_code=api_status.HTTP_409_CONFLICT, + content=_error_payload(code="TELL_MOUNT_FAILED", message=str(exc) or "TELL mount failed"), + ) + + @app.exception_handler(TellCommandWhileBusyException) + async def tell_busy_handler(request: Request, exc: TellCommandWhileBusyException) -> JSONResponse: + return JSONResponse( + status_code=api_status.HTTP_409_CONFLICT, + content=_error_payload(code="TELL_BUSY", message=str(exc) or "TELL is busy"), + ) + + @app.exception_handler(TellConnectionException) + async def tell_connection_error_handler(request: Request, exc: TellConnectionException) -> JSONResponse: + return JSONResponse( + status_code=api_status.HTTP_503_SERVICE_UNAVAILABLE, + content=_error_payload(code="TELL_CONNECTION_ERROR", message=str(exc) or "TELL connection error"), + ) + @app.exception_handler(SmargonCommunicationError) async def smargon_comm_handler(request: Request, exc: SmargonCommunicationError) -> JSONResponse: return JSONResponse( @@ -134,6 +208,21 @@ def register_exception_handlers(app) -> None: ), ) + @app.exception_handler(AareDBCommunicationError) + async def aaredb_comm_handler(request: Request, exc: AareDBCommunicationError) -> JSONResponse: + return JSONResponse( + status_code=api_status.HTTP_503_SERVICE_UNAVAILABLE, + content=_error_payload( + code="AAREDB_UNAVAILABLE", + message=str(exc) or "AareDB is unavailable", + extra={ + "operation": getattr(exc, "operation", None), + "endpoint": getattr(exc, "endpoint", None), + "base_url": getattr(exc, "base_url", None), + }, + ), + ) + @app.exception_handler(TellCommunicationError) async def tell_comm_handler(request: Request, exc: TellCommunicationError) -> JSONResponse: return JSONResponse( @@ -178,6 +267,16 @@ def register_exception_handlers(app) -> None: ), ) + @app.exception_handler(MagnetPositionSensorErorr) + async def magnet_position_sensor_handler(request: Request, exc: MagnetPositionSensorErorr) -> JSONResponse: + return JSONResponse( + status_code=api_status.HTTP_500_INTERNAL_SERVER_ERROR, + content=_error_payload( + code="MAGNET_POSITION_SENSOR_ERROR", + message=str(exc) or "Magnet position sensor error", + ), + ) + @app.exception_handler(Exception) async def unhandled_exception_handler(request: Request, exc: Exception) -> JSONResponse: logger.exception(f"Unhandled server exception: {exc}")