diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index 67aaa2035fc..cd53ccbd751 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -35,12 +35,12 @@ jobs: fail-fast: false matrix: os: ["ubuntu-latest"] # can add windows-latest, macos-latest - python: ["3.9", "3.10", "3.11"] + python: ["3.10", "3.11"] install: ["-e .[dev]"] # Make one version be non-editable to test both paths of version code include: - os: "ubuntu-latest" - python: "3.8" + python: "3.9" install: ".[dev]" runs-on: ${{ matrix.os }} diff --git a/src/dodal/devices/oav/snapshot.py b/src/dodal/devices/areadetector/plugins/MJPG.py similarity index 84% rename from src/dodal/devices/oav/snapshot.py rename to src/dodal/devices/areadetector/plugins/MJPG.py index 4574ddd53d2..14aebe237ca 100644 --- a/src/dodal/devices/oav/snapshot.py +++ b/src/dodal/devices/areadetector/plugins/MJPG.py @@ -6,14 +6,14 @@ from PIL import Image -class Snapshot(Device): +class MJPG(Device): filename: Signal = Component(Signal) directory: Signal = Component(Signal) url: EpicsSignal = Component(EpicsSignal, "JPG_URL_RBV", string=True) - x_size_pv: EpicsSignalRO = Component(EpicsSignalRO, "ArraySize1_RBV") - y_size_pv: EpicsSignalRO = Component(EpicsSignalRO, "ArraySize2_RBV") + x_size: EpicsSignalRO = Component(EpicsSignalRO, "ArraySize1_RBV") + y_size: EpicsSignalRO = Component(EpicsSignalRO, "ArraySize2_RBV") input_rbpv: EpicsSignalRO = Component(EpicsSignalRO, "NDArrayPort_RBV") - input_pv: EpicsSignal = Component(EpicsSignal, "NDArrayPort") + input_plugin: EpicsSignal = Component(EpicsSignal, "NDArrayPort") KICKOFF_TIMEOUT: float = 10.0 def trigger(self): diff --git a/src/dodal/devices/areadetector/plugins/MXSC.py b/src/dodal/devices/areadetector/plugins/MXSC.py new file mode 100644 index 00000000000..aa214fa451b --- /dev/null +++ b/src/dodal/devices/areadetector/plugins/MXSC.py @@ -0,0 +1,34 @@ +from ophyd import Component, Device, EpicsSignal + + +class MXSC(Device): + """ + Device for edge detection plugin. + """ + + input_plugin: EpicsSignal = Component(EpicsSignal, "NDArrayPort") + enable_callbacks: EpicsSignal = Component(EpicsSignal, "EnableCallbacks") + min_callback_time: EpicsSignal = Component(EpicsSignal, "MinCallbackTime") + blocking_callbacks: EpicsSignal = Component(EpicsSignal, "BlockingCallbacks") + read_file: EpicsSignal = Component(EpicsSignal, "ReadFile") + filename: EpicsSignal = Component(EpicsSignal, "Filename", string=True) + preprocess_operation: EpicsSignal = Component(EpicsSignal, "Preprocess") + preprocess_ksize: EpicsSignal = Component(EpicsSignal, "PpParam1") + canny_upper_threshold: EpicsSignal = Component(EpicsSignal, "CannyUpper") + canny_lower_threshold: EpicsSignal = Component(EpicsSignal, "CannyLower") + close_ksize: EpicsSignal = Component(EpicsSignal, "CloseKsize") + sample_detection_scan_direction: EpicsSignal = Component( + EpicsSignal, "ScanDirection" + ) + sample_detection_min_tip_height: EpicsSignal = Component( + EpicsSignal, "MinTipHeight" + ) + tip_x: EpicsSignal = Component(EpicsSignal, "TipX") + tip_y: EpicsSignal = Component(EpicsSignal, "TipY") + top: EpicsSignal = Component(EpicsSignal, "Top") + bottom: EpicsSignal = Component(EpicsSignal, "Bottom") + output_array: EpicsSignal = Component(EpicsSignal, "OutputArray") + draw_tip: EpicsSignal = Component(EpicsSignal, "DrawTip") + draw_edges: EpicsSignal = Component(EpicsSignal, "DrawEdges") + waveform_size_x: EpicsSignal = Component(EpicsSignal, "ArraySize1_RBV") + waveform_size_y: EpicsSignal = Component(EpicsSignal, "ArraySize2_RBV") diff --git a/src/dodal/devices/detector_motion.py b/src/dodal/devices/detector_motion.py index 9e602b50a7c..8ed564061fd 100644 --- a/src/dodal/devices/detector_motion.py +++ b/src/dodal/devices/detector_motion.py @@ -2,7 +2,7 @@ from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO -class Det(Device): +class DetectorMotion(Device): """Physical motion and interlocks for detector travel""" upstream_x: EpicsMotor = Cpt(EpicsMotor, "-MO-DET-01:UPSTREAMX") diff --git a/src/dodal/devices/oav/grid_overlay.py b/src/dodal/devices/oav/grid_overlay.py index 5f6885e24cc..9820ee19e1a 100644 --- a/src/dodal/devices/oav/grid_overlay.py +++ b/src/dodal/devices/oav/grid_overlay.py @@ -5,7 +5,7 @@ from ophyd import Component, Signal from PIL import Image, ImageDraw -from dodal.devices.oav.snapshot import Snapshot +from dodal.devices.areadetector.plugins.MJPG import MJPG class Orientation(Enum): @@ -120,19 +120,19 @@ def add_grid_overlay_to_image( ) -class SnapshotWithGrid(Snapshot): - top_left_x_signal: Signal = Component(Signal) - top_left_y_signal: Signal = Component(Signal) - box_width_signal: Signal = Component(Signal) - num_boxes_x_signal: Signal = Component(Signal) - num_boxes_y_signal: Signal = Component(Signal) +class SnapshotWithGrid(MJPG): + top_left_x: Signal = Component(Signal) + top_left_y: Signal = Component(Signal) + box_width: Signal = Component(Signal) + num_boxes_x: Signal = Component(Signal) + num_boxes_y: Signal = Component(Signal) def post_processing(self, image: Image.Image): - top_left_x = self.top_left_x_signal.get() - top_left_y = self.top_left_y_signal.get() - box_width = self.box_width_signal.get() - num_boxes_x = self.num_boxes_x_signal.get() - num_boxes_y = self.num_boxes_y_signal.get() + top_left_x = self.top_left_x.get() + top_left_y = self.top_left_y.get() + box_width = self.box_width.get() + num_boxes_x = self.num_boxes_x.get() + num_boxes_y = self.num_boxes_y.get() filename_str = self.filename.get() directory_str = self.directory.get() add_grid_border_overlay_to_image( diff --git a/src/dodal/devices/oav/oav_detector.py b/src/dodal/devices/oav/oav_detector.py index 6e13fd71855..9813d333ede 100644 --- a/src/dodal/devices/oav/oav_detector.py +++ b/src/dodal/devices/oav/oav_detector.py @@ -1,5 +1,3 @@ -from enum import IntEnum - from ophyd import ADComponent as ADC from ophyd import ( AreaDetector, @@ -13,24 +11,10 @@ ROIPlugin, ) +from dodal.devices.areadetector.plugins.MXSC import MXSC from dodal.devices.oav.grid_overlay import SnapshotWithGrid -class ColorMode(IntEnum): - """ - Enum to store the various color modes of the camera. We use RGB1. - """ - - MONO = 0 - BAYER = 1 - RGB1 = 2 - RGB2 = 3 - RGB3 = 4 - YUV444 = 5 - YUV422 = 6 - YUV421 = 7 - - class ZoomController(Device): """ Device to control the zoom level, this is unfortunately on a different prefix @@ -62,51 +46,6 @@ def allowed_zoom_levels(self): ] -class EdgeOutputArrayImageType(IntEnum): - """ - Enum to store the types of image to tweak the output array. We use Original. - """ - - ORIGINAL = 0 - GREYSCALE = 1 - PREPROCESSED = 2 - CANNY_EDGES = 3 - CLOSED_EDGES = 4 - - -class MXSC(Device): - """ - Device for edge detection plugin. - """ - - input_plugin_pv: EpicsSignal = Component(EpicsSignal, "NDArrayPort") - enable_callbacks_pv: EpicsSignal = Component(EpicsSignal, "EnableCallbacks") - min_callback_time_pv: EpicsSignal = Component(EpicsSignal, "MinCallbackTime") - blocking_callbacks_pv: EpicsSignal = Component(EpicsSignal, "BlockingCallbacks") - read_file: EpicsSignal = Component(EpicsSignal, "ReadFile") - py_filename: EpicsSignal = Component(EpicsSignal, "Filename", string=True) - preprocess_operation: EpicsSignal = Component(EpicsSignal, "Preprocess") - preprocess_ksize: EpicsSignal = Component(EpicsSignal, "PpParam1") - canny_upper_threshold: EpicsSignal = Component(EpicsSignal, "CannyUpper") - canny_lower_threshold: EpicsSignal = Component(EpicsSignal, "CannyLower") - close_ksize: EpicsSignal = Component(EpicsSignal, "CloseKsize") - sample_detection_scan_direction: EpicsSignal = Component( - EpicsSignal, "ScanDirection" - ) - sample_detection_min_tip_height: EpicsSignal = Component( - EpicsSignal, "MinTipHeight" - ) - tip_x: EpicsSignal = Component(EpicsSignal, "TipX") - tip_y: EpicsSignal = Component(EpicsSignal, "TipY") - top: EpicsSignal = Component(EpicsSignal, "Top") - bottom: EpicsSignal = Component(EpicsSignal, "Bottom") - output_array: EpicsSignal = Component(EpicsSignal, "OutputArray") - draw_tip: EpicsSignal = Component(EpicsSignal, "DrawTip") - draw_edges: EpicsSignal = Component(EpicsSignal, "DrawEdges") - waveform_size_x: EpicsSignal = Component(EpicsSignal, "ArraySize1_RBV") - waveform_size_y: EpicsSignal = Component(EpicsSignal, "ArraySize2_RBV") - - class OAV(AreaDetector): cam: CamBase = ADC(CamBase, "-DI-OAV-01:CAM:") roi: ADC = ADC(ROIPlugin, "-DI-OAV-01:ROI:") diff --git a/src/dodal/devices/oav/oav_parameters.py b/src/dodal/devices/oav/oav_parameters.py index ce5128256fa..9fd9412272e 100644 --- a/src/dodal/devices/oav/oav_parameters.py +++ b/src/dodal/devices/oav/oav_parameters.py @@ -1,6 +1,7 @@ import json import xml.etree.cElementTree as et -from typing import Tuple +from collections import ChainMap +from typing import Any, Tuple from dodal.devices.oav.oav_errors import ( OAVError_BeamPositionNotFound, @@ -8,124 +9,118 @@ ) from dodal.log import LOGGER +OAV_CONFIG_FILE_DEFAULTS = { + "zoom_params_file": "/dls_sw/i03/software/gda_versions/gda_9_27/workspace_git/gda-mx.git/configurations/i03-config/xml/jCameraManZoomLevels.xml", + "oav_config_json": "/dls_sw/i03/software/gda_versions/gda_9_27/workspace_git/gda-mx.git/configurations/i03-config/etc/OAVCentring.json", + "display_config": "/dls_sw/i03/software/gda_versions/var/display.configuration", +} + class OAVParameters: + zoom_params_file: str + oav_config_json: str + display_config: str + global_params: dict[str, Any] + context_dicts: dict[str, dict] + active_params: ChainMap + + exposure: float + acquire_period: float + gain: float + canny_edge_upper_threshold: float + canny_edge_lower_threshold: float + minimum_height: int + zoom: float + preprocess: int # gets blur type, e.g. 8 = gaussianBlur, 9 = medianBlur + preprocess_K_size: int # length scale for blur preprocessing + detection_script_filename: str + close_ksize: int + input_plugin: str + mxsc_input: str + min_callback_time: float + direction: int + max_tip_distance: float + def __init__( self, - centring_params_json: str, - camera_zoom_levels_file: str, - display_configuration_file: str, context="loopCentring", + zoom_params_file=OAV_CONFIG_FILE_DEFAULTS["zoom_params_file"], + oav_config_json=OAV_CONFIG_FILE_DEFAULTS["oav_config_json"], + display_config=OAV_CONFIG_FILE_DEFAULTS["display_config"], ): - self.centring_params_json = centring_params_json - self.camera_zoom_levels_file = camera_zoom_levels_file - self.display_configuration_file = display_configuration_file + self.zoom_params_file = zoom_params_file + self.oav_config_json = oav_config_json + self.display_config = display_config self.context = context - self.load_parameters_from_json() + self.global_params, self.context_dicts = self.load_json(self.oav_config_json) + self.active_params = ChainMap( + {}, self.global_params, self.context_dicts[self.context] + ) + self.update_self_from_current_context() self.load_microns_per_pixel() self._extract_beam_position() - def load_json(self): - """ - Loads the json from the json file at self.centring_params_json and save it as a dictionary in the parameters attribute. - """ - with open(f"{self.centring_params_json}") as f: - self.parameters = json.load(f) - - def load_parameters_from_json( - self, - ) -> None: - """ - Load required parameters on initialisation as an attribute variables. If a variable in the json is - liable to change throughout a run it can be reloaded when needed. - """ - - self.load_json() - - self.exposure = self._extract_dict_parameter("exposure") - self.acquire_period = self._extract_dict_parameter("acqPeriod") - self.gain = self._extract_dict_parameter("gain") - self.canny_edge_upper_threshold = self._extract_dict_parameter( - "CannyEdgeUpperThreshold" - ) - self.canny_edge_lower_threshold = self._extract_dict_parameter( - "CannyEdgeLowerThreshold", fallback_value=5.0 - ) - self.minimum_height = self._extract_dict_parameter("minheight") - self.zoom = self._extract_dict_parameter("zoom") - # gets blur type, e.g. 8 = gaussianBlur, 9 = medianBlur - self.preprocess = self._extract_dict_parameter("preprocess") - # length scale for blur preprocessing - self.preprocess_K_size = self._extract_dict_parameter("preProcessKSize") - self.filename = self._extract_dict_parameter("filename") - self.close_ksize = self._extract_dict_parameter( - "close_ksize", fallback_value=11 - ) - - self.input_plugin = self._extract_dict_parameter("oav", fallback_value="OAV") - self.mxsc_input = self._extract_dict_parameter( - "mxsc_input", fallback_value="CAM" - ) - self.min_callback_time = self._extract_dict_parameter( - "min_callback_time", fallback_value=0.08 - ) - - self.direction = self._extract_dict_parameter("direction") - - self.max_tip_distance = self._extract_dict_parameter("max_tip_distance") - - def _extract_dict_parameter(self, key: str, fallback_value=None, reload_json=False): + @staticmethod + def load_json(filename: str) -> tuple[dict[str, Any], dict[str, dict]]: """ - Designed to extract parameters from the json OAVParameters.json. This will hopefully be changed in - future, but currently we have to use the json passed in from GDA. - - The json is of the form: - { - "parameter1": value1, - "parameter2": value2, - "context_name": { - "parameter1": value3 - } - When we extract the parameters we want to check if the given context (stored as a class variable) - contains a parameter, if it does we return it, if not we return the global value. If a parameter - is not found at all then the passed in fallback_value is returned. If that isn't found then an - error is raised. - Args: - key: the key of the value being extracted - fallback_value: a value to be returned if the key is not found - reload_json: reload the json from the file before searching for it, needed because some - parameters can change mid operation. - Returns: The extracted value corresponding to the key, or the fallback_value if none is found. + Loads the json from the specified file, and returns a dict with all the + individual top-level k-v pairs, and one with all the subdicts. """ + with open(filename) as f: + raw_params: dict[str, Any] = json.load(f) + global_params = { + k: raw_params.pop(k) + for k, v in list(raw_params.items()) + if not isinstance(v, dict) + } + context_dicts = raw_params + return global_params, context_dicts + + def update_context(self, context: str) -> None: + self.active_params.maps.pop() + self.active_params = self.active_params.new_child(self.context_dicts[context]) + + def update_self_from_current_context(self) -> None: + def update(name, param_type, default=None): + param = self.active_params.get(name, default) + try: + param = param_type(param) + return param + except AssertionError: + raise TypeError( + f"OAV param {name} from the OAV centring params json file has the " + f"wrong type, should be {param_type} but is {type(param)}." + ) - if reload_json: - self.load_json() - - if self.context in self.parameters: - if key in self.parameters[self.context]: - return self.parameters[self.context][key] - - if key in self.parameters: - return self.parameters[key] - - if fallback_value: - return fallback_value - - # No fallback_value was given and the key wasn't found. - raise KeyError( - f"Searched in {self.centring_params_json} for key {key} in context {self.context} but no value was found. No fallback value was given." + self.exposure = update("exposure", float) + self.acquire_period = update("acqPeriod", float) + self.gain = update("gain", float) + self.canny_edge_upper_threshold = update("CannyEdgeUpperThreshold", float) + self.canny_edge_lower_threshold = update( + "CannyEdgeLowerThreshold", float, default=5.0 ) + self.minimum_height = update("minheight", int) + self.zoom = update("zoom", float) + self.preprocess = update("preprocess", int) + self.preprocess_K_size = update("preProcessKSize", int) + self.detection_script_filename = update("filename", str) + self.close_ksize = update("close_ksize", int, default=11) + self.input_plugin = update("oav", str, default="OAV") + self.mxsc_input = update("mxsc_input", str, default="CAM") + self.min_callback_time = update("min_callback_time", float, default=0.08) + self.direction = update("direction", int) + self.max_tip_distance = update("max_tip_distance", float) def load_microns_per_pixel(self, zoom=None): """ - Loads the microns per x pixel and y pixel for a given zoom level. These are currently generated by GDA, though artemis could generate them - in future. + Loads the microns per x pixel and y pixel for a given zoom level. These are + currently generated by GDA, though artemis could generate them in future. """ if not zoom: zoom = self.zoom - tree = et.parse(self.camera_zoom_levels_file) + tree = et.parse(self.zoom_params_file) self.micronsPerXPixel = self.micronsPerYPixel = None root = tree.getroot() levels = root.findall(".//zoomLevel") @@ -135,7 +130,7 @@ def load_microns_per_pixel(self, zoom=None): self.micronsPerYPixel = float(node.find("micronsPerYPixel").text) if self.micronsPerXPixel is None or self.micronsPerYPixel is None: raise OAVError_ZoomLevelNotFound( - f"Could not find the micronsPer[X,Y]Pixel parameters in {self.camera_zoom_levels_file} for zoom level {zoom}." + f"Could not find the micronsPer[X,Y]Pixel parameters in {self.zoom_params_file} for zoom level {zoom}." ) # get the max tip distance in pixels @@ -147,7 +142,7 @@ def _extract_beam_position(self): stored in the file display.configuration. The beam location is manually inputted by the beamline operator GDA by clicking where on screen a scintillator ligths up. """ - with open(self.display_configuration_file, "r") as f: + with open(self.display_config, "r") as f: file_lines = f.readlines() for i in range(len(file_lines)): if file_lines[i].startswith("zoomLevel = " + str(self.zoom)): diff --git a/src/dodal/devices/oav/utils.py b/src/dodal/devices/oav/utils.py index 7753784e47b..3fc47ca3d05 100644 --- a/src/dodal/devices/oav/utils.py +++ b/src/dodal/devices/oav/utils.py @@ -1,3 +1,5 @@ +from enum import IntEnum + from dodal.utils import Point2D @@ -15,3 +17,30 @@ def bottom_right_from_top_left( int(steps_x * step_size_x * 1000 * pix_per_um_x + top_left.x), int(steps_y * step_size_y * 1000 * pix_per_um_y + top_left.y), ) + + +class ColorMode(IntEnum): + """ + Enum to store the various color modes of the camera. We use RGB1. + """ + + MONO = 0 + BAYER = 1 + RGB1 = 2 + RGB2 = 3 + RGB3 = 4 + YUV444 = 5 + YUV422 = 6 + YUV421 = 7 + + +class EdgeOutputArrayImageType(IntEnum): + """ + Enum to store the types of image to tweak the output array. We use Original. + """ + + ORIGINAL = 0 + GREYSCALE = 1 + PREPROCESSED = 2 + CANNY_EDGES = 3 + CLOSED_EDGES = 4 diff --git a/src/dodal/i03.py b/src/dodal/i03.py index 3e0233d33f5..8c4b72ea8d8 100644 --- a/src/dodal/i03.py +++ b/src/dodal/i03.py @@ -7,6 +7,7 @@ from dodal.devices.backlight import Backlight from dodal.devices.DCM import DCM from dodal.devices.detector import DetectorParams +from dodal.devices.detector_motion import DetectorMotion from dodal.devices.eiger import EigerDetector from dodal.devices.fast_grid_scan import FastGridScan from dodal.devices.oav.oav_detector import OAV @@ -131,6 +132,22 @@ def backlight( ) +@skip_device(lambda: BL == "s03") +def detector_motion( + wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False +) -> DetectorMotion: + """Get the i03 detector motion device, instantiate it if it hasn't already been. + If this is called when already instantiated in i03, it will return the existing object. + """ + return device_instantiation( + device=DetectorMotion, + name="detector_motion", + prefix="", + wait=wait_for_connection, + fake=fake_with_ophyd_sim, + ) + + @skip_device(lambda: BL == "s03") def eiger( wait_for_connection: bool = True, diff --git a/src/dodal/utils.py b/src/dodal/utils.py index 195e53ffa33..84bca212279 100644 --- a/src/dodal/utils.py +++ b/src/dodal/utils.py @@ -7,7 +7,7 @@ from inspect import signature from os import environ from types import ModuleType -from typing import Any, Callable, Dict, Iterable, Optional, Type, Union +from typing import Any, Callable, Dict, Iterable, Optional, Type, TypeVar, Union from bluesky.protocols import ( Checkable, @@ -48,6 +48,8 @@ Point2D = namedtuple("Point2D", ["x", "y"]) Point3D = namedtuple("Point3D", ["x", "y", "z"]) +T = TypeVar("T") + def get_beamline_name(ixx: str) -> str: bl = environ.get("BEAMLINE") @@ -73,9 +75,9 @@ def __post_init__(self): def skip_device(precondition=lambda: True): - def decorator(func: Callable[..., Any]): + def decorator(func: Callable[..., T]) -> Callable[..., T]: @wraps(func) - def wrapper(*args, **kwds): + def wrapper(*args, **kwds) -> T: return func(*args, **kwds) if precondition: diff --git a/tests/devices/system_tests/test_oav_system.py b/tests/devices/system_tests/test_oav_system.py index a1a2656cfcb..aaa5a116477 100644 --- a/tests/devices/system_tests/test_oav_system.py +++ b/tests/devices/system_tests/test_oav_system.py @@ -13,11 +13,11 @@ def take_snapshot_with_grid(oav: OAV, snapshot_filename, snapshot_directory): oav.wait_for_connection() - yield from bps.abs_set(oav.snapshot.top_left_x_signal, TEST_GRID_TOP_LEFT_X) - yield from bps.abs_set(oav.snapshot.top_left_y_signal, TEST_GRID_TOP_LEFT_Y) - yield from bps.abs_set(oav.snapshot.box_width_signal, TEST_GRID_BOX_WIDTH) - yield from bps.abs_set(oav.snapshot.num_boxes_x_signal, TEST_GRID_NUM_BOXES_X) - yield from bps.abs_set(oav.snapshot.num_boxes_y_signal, TEST_GRID_NUM_BOXES_Y) + yield from bps.abs_set(oav.snapshot.top_left_x, TEST_GRID_TOP_LEFT_X) + yield from bps.abs_set(oav.snapshot.top_left_y, TEST_GRID_TOP_LEFT_Y) + yield from bps.abs_set(oav.snapshot.box_width, TEST_GRID_BOX_WIDTH) + yield from bps.abs_set(oav.snapshot.num_boxes_x, TEST_GRID_NUM_BOXES_X) + yield from bps.abs_set(oav.snapshot.num_boxes_y, TEST_GRID_NUM_BOXES_Y) yield from bps.abs_set(oav.snapshot.filename, snapshot_filename) yield from bps.abs_set(oav.snapshot.directory, snapshot_directory) yield from bps.trigger(oav.snapshot, wait=True) diff --git a/tests/devices/unit_tests/test_oav.py b/tests/devices/unit_tests/test_oav.py index 57b18766e08..0d3a65785ab 100644 --- a/tests/devices/unit_tests/test_oav.py +++ b/tests/devices/unit_tests/test_oav.py @@ -19,11 +19,11 @@ def fake_oav() -> OAV: fake_oav.snapshot.url.sim_put("http://test.url") fake_oav.snapshot.filename.put("test filename") fake_oav.snapshot.directory.put("test directory") - fake_oav.snapshot.top_left_x_signal.put(100) - fake_oav.snapshot.top_left_y_signal.put(100) - fake_oav.snapshot.box_width_signal.put(50) - fake_oav.snapshot.num_boxes_x_signal.put(15) - fake_oav.snapshot.num_boxes_y_signal.put(10) + fake_oav.snapshot.top_left_x.put(100) + fake_oav.snapshot.top_left_y.put(100) + fake_oav.snapshot.box_width.put(50) + fake_oav.snapshot.num_boxes_x.put(15) + fake_oav.snapshot.num_boxes_y.put(10) return fake_oav @@ -41,15 +41,17 @@ def test_snapshot_trigger_handles_request_with_bad_status_code_correctly( @patch("requests.get") -@patch("dodal.devices.oav.snapshot.Image") -def test_snapshot_trigger_loads_correct_url(mock_image, mock_get: MagicMock, fake_oav): +@patch("dodal.devices.areadetector.plugins.MJPG.Image") +def test_snapshot_trigger_loads_correct_url( + mock_image: MagicMock, mock_get: MagicMock, fake_oav: OAV +): st = fake_oav.snapshot.trigger() st.wait() mock_get.assert_called_once_with("http://test.url", stream=True) @patch("requests.get") -@patch("dodal.devices.oav.snapshot.Image.open") +@patch("dodal.devices.areadetector.plugins.MJPG.Image.open") def test_snapshot_trigger_saves_to_correct_file( mock_open: MagicMock, mock_get, fake_oav ): @@ -68,7 +70,7 @@ def test_snapshot_trigger_saves_to_correct_file( @patch("requests.get") -@patch("dodal.devices.oav.snapshot.Image.open") +@patch("dodal.devices.areadetector.plugins.MJPG.Image.open") @patch("dodal.devices.oav.grid_overlay.add_grid_overlay_to_image") @patch("dodal.devices.oav.grid_overlay.add_grid_border_overlay_to_image") def test_correct_grid_drawn_on_image( diff --git a/tests/devices/unit_tests/test_oav_centring.py b/tests/devices/unit_tests/test_oav_centring.py index c93b13f4f96..33762668bcb 100644 --- a/tests/devices/unit_tests/test_oav_centring.py +++ b/tests/devices/unit_tests/test_oav_centring.py @@ -44,7 +44,9 @@ def mock_oav(): @pytest.fixture def mock_parameters(): - return OAVParameters(OAV_CENTRING_JSON, ZOOM_LEVELS_XML, DISPLAY_CONFIGURATION) + return OAVParameters( + "loopCentring", ZOOM_LEVELS_XML, OAV_CENTRING_JSON, DISPLAY_CONFIGURATION + ) @pytest.fixture @@ -91,31 +93,9 @@ def fake_run( def test_oav_parameters_load_parameters_from_json( parameter_name, expected_value, mock_parameters: OAVParameters ): - mock_parameters.load_parameters_from_json() - assert mock_parameters.__dict__[parameter_name] == expected_value -def test_oav__extract_dict_parameter_not_found_fallback_value_present( - mock_parameters: OAVParameters, -): - mock_parameters.load_json() - assert ( - mock_parameters._extract_dict_parameter( - "a_key_not_in_the_json", fallback_value=1 - ) - == 1 - ) - - -def test_oav__extract_dict_parameter_not_found_fallback_value_not_present( - mock_parameters: OAVParameters, -): - mock_parameters.load_json() - with pytest.raises(KeyError): - mock_parameters._extract_dict_parameter("a_key_not_in_the_json") - - def test_find_midpoint_symmetric_pin(): x = np.arange(-15, 10, 25 / 1024) x2 = x**2