From 36ff602d65001d49ca93e8173beadf641a542cfd Mon Sep 17 00:00:00 2001 From: Noemi Frisina Date: Wed, 25 Feb 2026 16:57:25 +0000 Subject: [PATCH 1/4] First try just leaving everything as is --- src/dodal/beamlines/i19_1.py | 9 +++++++++ src/dodal/devices/beamlines/i04/beam_centre.py | 1 + 2 files changed, 10 insertions(+) diff --git a/src/dodal/beamlines/i19_1.py b/src/dodal/beamlines/i19_1.py index 3e8ef966622..2009c1545a9 100644 --- a/src/dodal/beamlines/i19_1.py +++ b/src/dodal/beamlines/i19_1.py @@ -2,6 +2,7 @@ set_beamline as set_utils_beamline, ) from dodal.device_manager import DeviceManager +from dodal.devices.beamlines.i04.beam_centre import CentreEllipseMethod from dodal.devices.beamlines.i19.access_controlled.attenuator_motor_squad import ( AttenuatorMotorSquad, ) @@ -60,6 +61,14 @@ def beamstop() -> BeamStop: return BeamStop(prefix=f"{PREFIX.beamline_prefix}-RS-ABSB-01:") +@devices.factory() +def beam_centre() -> CentreEllipseMethod: + return CentreEllipseMethod( + prefix=f"{PREFIX.beamline_prefix}-EA-OAV-01:", + overlay_channel=7, + ) + + @devices.fixture def oav_config() -> OAVConfigBeamCentre: return OAVConfigBeamCentre(ZOOM_PARAMS_FILE, DISPLAY_CONFIG) diff --git a/src/dodal/devices/beamlines/i04/beam_centre.py b/src/dodal/devices/beamlines/i04/beam_centre.py index c1948830c98..daddd9ba470 100644 --- a/src/dodal/devices/beamlines/i04/beam_centre.py +++ b/src/dodal/devices/beamlines/i04/beam_centre.py @@ -146,6 +146,7 @@ async def trigger(self): ellipse_fit = self._fit_ellipse(roi_binary) roi_centre_x = ellipse_fit[0][0] roi_centre_y = ellipse_fit[0][1] + LOGGER.info(f"Beam centre founf at ({roi_centre_x}, {roi_centre_y})") # convert back to full screen image coords and set beam centre self._center_x_val_setter(roi_centre_x + top_left_corner[0]) self._center_y_val_setter(roi_centre_y + top_left_corner[1]) From 57cf794fa6d0daee9239f5700527f528a4306cea Mon Sep 17 00:00:00 2001 From: Noemi Frisina Date: Wed, 25 Feb 2026 17:03:12 +0000 Subject: [PATCH 2/4] Move beam_centre device to a more common location and instantiate on i19-1 --- src/dodal/beamlines/i04.py | 2 +- src/dodal/beamlines/i19_1.py | 2 +- src/dodal/devices/oav/beam_centre/__init__.py | 0 .../devices/{beamlines/i04 => oav/beam_centre}/beam_centre.py | 0 tests/devices/{beamlines/i04 => oav}/test_beam_centre.py | 2 +- 5 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 src/dodal/devices/oav/beam_centre/__init__.py rename src/dodal/devices/{beamlines/i04 => oav/beam_centre}/beam_centre.py (100%) rename tests/devices/{beamlines/i04 => oav}/test_beam_centre.py (99%) diff --git a/src/dodal/beamlines/i04.py b/src/dodal/beamlines/i04.py index ef1ef5fdbb4..782dd4357cc 100644 --- a/src/dodal/beamlines/i04.py +++ b/src/dodal/beamlines/i04.py @@ -12,7 +12,6 @@ from dodal.devices.backlight import Backlight from dodal.devices.baton import Baton from dodal.devices.beamlines.i03.dcm import DCM -from dodal.devices.beamlines.i04.beam_centre import CentreEllipseMethod from dodal.devices.beamlines.i04.beamsize import Beamsize from dodal.devices.beamlines.i04.constants import RedisConstants from dodal.devices.beamlines.i04.max_pixel import MaxPixel @@ -26,6 +25,7 @@ from dodal.devices.ipin import IPin from dodal.devices.motors import XYZStage from dodal.devices.mx_phase1.beamstop import Beamstop +from dodal.devices.oav.beam_centre.beam_centre import CentreEllipseMethod from dodal.devices.oav.oav_detector import ( OAVBeamCentrePV, ZoomControllerWithBeamCentres, diff --git a/src/dodal/beamlines/i19_1.py b/src/dodal/beamlines/i19_1.py index 2009c1545a9..e67ab3eb6ad 100644 --- a/src/dodal/beamlines/i19_1.py +++ b/src/dodal/beamlines/i19_1.py @@ -2,7 +2,6 @@ set_beamline as set_utils_beamline, ) from dodal.device_manager import DeviceManager -from dodal.devices.beamlines.i04.beam_centre import CentreEllipseMethod from dodal.devices.beamlines.i19.access_controlled.attenuator_motor_squad import ( AttenuatorMotorSquad, ) @@ -16,6 +15,7 @@ ) from dodal.devices.beamlines.i19.beamstop import BeamStop from dodal.devices.beamlines.i19.pin_tip import PinTipCentreHolder +from dodal.devices.oav.beam_centre.beam_centre import CentreEllipseMethod from dodal.devices.oav.oav_detector import OAVBeamCentreFile from dodal.devices.oav.oav_parameters import OAVConfigBeamCentre from dodal.devices.oav.pin_image_recognition import PinTipDetection diff --git a/src/dodal/devices/oav/beam_centre/__init__.py b/src/dodal/devices/oav/beam_centre/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/dodal/devices/beamlines/i04/beam_centre.py b/src/dodal/devices/oav/beam_centre/beam_centre.py similarity index 100% rename from src/dodal/devices/beamlines/i04/beam_centre.py rename to src/dodal/devices/oav/beam_centre/beam_centre.py diff --git a/tests/devices/beamlines/i04/test_beam_centre.py b/tests/devices/oav/test_beam_centre.py similarity index 99% rename from tests/devices/beamlines/i04/test_beam_centre.py rename to tests/devices/oav/test_beam_centre.py index 5454e735f11..91fcbe0cf62 100644 --- a/tests/devices/beamlines/i04/test_beam_centre.py +++ b/tests/devices/oav/test_beam_centre.py @@ -5,7 +5,7 @@ import pytest from ophyd_async.core import init_devices, set_mock_value -from dodal.devices.beamlines.i04.beam_centre import ( +from dodal.devices.oav.beam_centre.beam_centre import ( CentreEllipseMethod, convert_image_to_binary, get_roi, From 8630165eea483fe48e12c720d30697d6ed4a108f Mon Sep 17 00:00:00 2001 From: Noemi Frisina Date: Wed, 25 Feb 2026 17:13:15 +0000 Subject: [PATCH 3/4] Actually fix tests --- tests/devices/oav/test_beam_centre.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/devices/oav/test_beam_centre.py b/tests/devices/oav/test_beam_centre.py index 91fcbe0cf62..07b32565c56 100644 --- a/tests/devices/oav/test_beam_centre.py +++ b/tests/devices/oav/test_beam_centre.py @@ -35,8 +35,8 @@ def test_image_array(): ) -@patch("dodal.devices.beamlines.i04.beam_centre.cv2.threshold") -@patch("dodal.devices.beamlines.i04.beam_centre.convert_to_gray_and_blur") +@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.cv2.threshold") +@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.convert_to_gray_and_blur") async def test_convert_image_to_binary_calls_threshold_twice( mock_convert, mock_threshold ): @@ -60,8 +60,8 @@ async def test_convert_image_to_binary_calls_threshold_twice( assert second_call_args[1] == 147 -@patch("dodal.devices.beamlines.i04.beam_centre.cv2.findContours") -@patch("dodal.devices.beamlines.i04.beam_centre.cv2.fitEllipse") +@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.cv2.findContours") +@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.cv2.fitEllipse") async def test_fit_ellipse_good_params( fit_ellipse: MagicMock, find_contours_mock: MagicMock, @@ -95,7 +95,7 @@ async def test_fit_ellipse_raises_error_if_not_enough_image_points( ], # 4 points (square) ], ) -@patch("dodal.devices.beamlines.i04.beam_centre.cv2.findContours") +@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.cv2.findContours") async def test_fit_ellipse_raises_error_if_not_enough_contour_points( find_contours_mock: MagicMock, centre_device: CentreEllipseMethod, @@ -108,8 +108,10 @@ async def test_fit_ellipse_raises_error_if_not_enough_contour_points( await centre_device.trigger() -@patch("dodal.devices.beamlines.i04.beam_centre.CentreEllipseMethod._fit_ellipse") -@patch("dodal.devices.beamlines.i04.beam_centre.convert_image_to_binary") +@patch( + "dodal.devices.beamlines.oav.beam_centre.beam_centre.CentreEllipseMethod._fit_ellipse" +) +@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.convert_image_to_binary") async def test_trigger_converts_to_binary_then_finds_ellipse( mock_convert_to_binary: MagicMock, mock_fit_ellipse: MagicMock, From 6e2ef54e8f47b6a791c3fb23c52b504d0e1f6cb9 Mon Sep 17 00:00:00 2001 From: Noemi Frisina Date: Wed, 25 Feb 2026 17:28:52 +0000 Subject: [PATCH 4/4] That was not a fix - this hopefully is --- tests/devices/oav/test_beam_centre.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/devices/oav/test_beam_centre.py b/tests/devices/oav/test_beam_centre.py index 07b32565c56..ec9f37ee573 100644 --- a/tests/devices/oav/test_beam_centre.py +++ b/tests/devices/oav/test_beam_centre.py @@ -35,8 +35,8 @@ def test_image_array(): ) -@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.cv2.threshold") -@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.convert_to_gray_and_blur") +@patch("dodal.devices.oav.beam_centre.beam_centre.cv2.threshold") +@patch("dodal.devices.oav.beam_centre.beam_centre.convert_to_gray_and_blur") async def test_convert_image_to_binary_calls_threshold_twice( mock_convert, mock_threshold ): @@ -60,8 +60,8 @@ async def test_convert_image_to_binary_calls_threshold_twice( assert second_call_args[1] == 147 -@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.cv2.findContours") -@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.cv2.fitEllipse") +@patch("dodal.devices.oav.beam_centre.beam_centre.cv2.findContours") +@patch("dodal.devices.oav.beam_centre.beam_centre.cv2.fitEllipse") async def test_fit_ellipse_good_params( fit_ellipse: MagicMock, find_contours_mock: MagicMock, @@ -95,7 +95,7 @@ async def test_fit_ellipse_raises_error_if_not_enough_image_points( ], # 4 points (square) ], ) -@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.cv2.findContours") +@patch("dodal.devices.oav.beam_centre.beam_centre.cv2.findContours") async def test_fit_ellipse_raises_error_if_not_enough_contour_points( find_contours_mock: MagicMock, centre_device: CentreEllipseMethod, @@ -108,10 +108,8 @@ async def test_fit_ellipse_raises_error_if_not_enough_contour_points( await centre_device.trigger() -@patch( - "dodal.devices.beamlines.oav.beam_centre.beam_centre.CentreEllipseMethod._fit_ellipse" -) -@patch("dodal.devices.beamlines.oav.beam_centre.beam_centre.convert_image_to_binary") +@patch("dodal.devices.oav.beam_centre.beam_centre.CentreEllipseMethod._fit_ellipse") +@patch("dodal.devices.oav.beam_centre.beam_centre.convert_image_to_binary") async def test_trigger_converts_to_binary_then_finds_ellipse( mock_convert_to_binary: MagicMock, mock_fit_ellipse: MagicMock,