Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into remove_i03_xbpm_timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
olliesilvester committed Nov 28, 2023
2 parents 4353f39 + 0d960bb commit 3b1557e
Show file tree
Hide file tree
Showing 15 changed files with 516 additions and 163 deletions.
8 changes: 7 additions & 1 deletion src/dodal/beamlines/i03.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from dodal.devices.eiger import EigerDetector
from dodal.devices.fast_grid_scan import FastGridScan
from dodal.devices.flux import Flux
from dodal.devices.oav.oav_detector import OAV
from dodal.devices.oav.oav_detector import OAV, OAVConfigParams
from dodal.devices.oav.pin_image_recognition import PinTipDetection
from dodal.devices.s4_slit_gaps import S4SlitGaps
from dodal.devices.sample_shutter import SampleShutter
Expand All @@ -24,6 +24,11 @@
from dodal.log import set_beamline as set_log_beamline
from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device

ZOOM_PARAMS_FILE = (
"/dls_sw/i03/software/gda/configurations/i03-config/xml/jCameraManZoomLevels.xml"
)
DISPLAY_CONFIG = "/dls_sw/i03/software/gda_versions/var/display.configuration"

BL = get_beamline_name("s03")
set_log_beamline(BL)
set_utils_beamline(BL)
Expand Down Expand Up @@ -149,6 +154,7 @@ def oav(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) ->
"",
wait_for_connection,
fake_with_ophyd_sim,
params=OAVConfigParams(ZOOM_PARAMS_FILE, DISPLAY_CONFIG),
)


Expand Down
8 changes: 7 additions & 1 deletion src/dodal/beamlines/i04.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from dodal.devices.i04.transfocator import Transfocator
from dodal.devices.ipin import IPin
from dodal.devices.motors import XYZPositioner
from dodal.devices.oav.oav_detector import OAV
from dodal.devices.oav.oav_detector import OAV, OAVConfigParams
from dodal.devices.s4_slit_gaps import S4SlitGaps
from dodal.devices.sample_shutter import SampleShutter
from dodal.devices.smargon import Smargon
Expand All @@ -26,6 +26,11 @@
from dodal.log import set_beamline as set_log_beamline
from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device

ZOOM_PARAMS_FILE = (
"/dls_sw/i04/software/gda/configurations/i04-config/xml/jCameraManZoomLevels.xml"
)
DISPLAY_CONFIG = "/dls_sw/i04/software/gda_versions/var/display.configuration"

BL = get_beamline_name("s04")
set_log_beamline(BL)
set_utils_beamline(BL)
Expand Down Expand Up @@ -341,6 +346,7 @@ def oav(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) ->
"",
wait_for_connection,
fake_with_ophyd_sim,
params=OAVConfigParams(ZOOM_PARAMS_FILE, DISPLAY_CONFIG),
)


Expand Down
6 changes: 5 additions & 1 deletion src/dodal/beamlines/i04_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@
from dodal.devices.backlight import Backlight
from dodal.devices.detector import DetectorParams
from dodal.devices.eiger import EigerDetector
from dodal.devices.oav.oav_detector import OAV
from dodal.devices.oav.oav_detector import OAV, OAVConfigParams
from dodal.devices.s4_slit_gaps import S4SlitGaps
from dodal.devices.synchrotron import Synchrotron
from dodal.devices.undulator import Undulator
from dodal.devices.zebra import Zebra
from dodal.log import set_beamline as set_log_beamline
from dodal.utils import BeamlinePrefix, get_beamline_name, skip_device

ZOOM_PARAMS_FILE = "/dls_sw/i04-1/software/gda/config/xml/jCameraManZoomLevels.xml"
DISPLAY_CONFIG = "/dls_sw/i04-1/software/gda_versions/var/display.configuration"

_simulator_beamline_fallback = "s04_1"
BL = get_beamline_name(_simulator_beamline_fallback)
set_log_beamline(BL)
Expand Down Expand Up @@ -74,6 +77,7 @@ def oav(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) ->
"",
wait_for_connection,
fake_with_ophyd_sim,
params=OAVConfigParams(ZOOM_PARAMS_FILE, DISPLAY_CONFIG),
)


Expand Down
6 changes: 5 additions & 1 deletion src/dodal/beamlines/i24.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@
from dodal.devices.i24.dual_backlight import DualBacklight
from dodal.devices.i24.I24_detector_motion import DetectorMotion
from dodal.devices.i24.i24_vgonio import VGonio
from dodal.devices.oav.oav_detector import OAV
from dodal.devices.oav.oav_detector import OAV, OAVConfigParams
from dodal.devices.zebra import Zebra
from dodal.log import set_beamline
from dodal.utils import get_beamline_name, skip_device

ZOOM_PARAMS_FILE = "/dls_sw/i24/software/gda/config/xml/jCameraManZoomLevels.xml"
DISPLAY_CONFIG = "/dls_sw/i24/software/gda_versions/var/display.configuration"

BL = get_beamline_name("s24")
set_beamline(BL)
set_utils_beamline(BL)
Expand Down Expand Up @@ -84,6 +87,7 @@ def oav(wait_for_connection: bool = True, fake_with_ophyd_sim: bool = False) ->
"",
wait_for_connection,
fake_with_ophyd_sim,
params=OAVConfigParams(ZOOM_PARAMS_FILE, DISPLAY_CONFIG),
)


Expand Down
2 changes: 1 addition & 1 deletion src/dodal/devices/DCM.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class DCM(Device):
roll: EpicsMotor = Cpt(EpicsMotor, "-MO-DCM-01:ROLL")
offset: EpicsMotor = Cpt(EpicsMotor, "-MO-DCM-01:OFFSET")
perp: EpicsMotor = Cpt(EpicsMotor, "-MO-DCM-01:PERP")
energy: EpicsMotor = Cpt(EpicsMotor, "-MO-DCM-01:ENERGY", kind=Kind.hinted)
energy_in_kev: EpicsMotor = Cpt(EpicsMotor, "-MO-DCM-01:ENERGY", kind=Kind.hinted)
pitch: EpicsMotor = Cpt(EpicsMotor, "-MO-DCM-01:PITCH")
wavelength: EpicsMotor = Cpt(EpicsMotor, "-MO-DCM-01:WAVELENGTH")

Expand Down
101 changes: 101 additions & 0 deletions src/dodal/devices/oav/oav_detector.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import xml.etree.cElementTree as et
from typing import Tuple

from ophyd import ADComponent as ADC
from ophyd import (
AreaDetector,
Expand All @@ -15,6 +18,11 @@

from dodal.devices.areadetector.plugins.MXSC import MXSC
from dodal.devices.oav.grid_overlay import SnapshotWithGrid
from dodal.devices.oav.oav_errors import (
OAVError_BeamPositionNotFound,
OAVError_ZoomLevelNotFound,
)
from dodal.log import LOGGER


class ZoomController(Device):
Expand Down Expand Up @@ -70,6 +78,94 @@ def set(self, level_to_set: str) -> StatusBase:
return return_status


class OAVConfigParams:
"""
The OAV parameters which may update depending on settings such as the zoom level.
"""

def __init__(
self,
zoom_params_file,
display_config,
):
self.zoom_params_file: str = zoom_params_file
self.display_config: str = display_config

def update_on_zoom(self, value, *args, **kwargs):
if isinstance(value, str) and value.endswith("x"):
value = value.strip("x")
zoom = float(value)
self.load_microns_per_pixel(zoom)
self.beam_centre_i, self.beam_centre_j = self.get_beam_position_from_zoom(zoom)

def load_microns_per_pixel(self, zoom: float):
"""
Loads the microns per x pixel and y pixel for a given zoom level. These are
currently generated by GDA, though hyperion could generate them in future.
"""
tree = et.parse(self.zoom_params_file)
self.micronsPerXPixel = self.micronsPerYPixel = None
root = tree.getroot()
levels = root.findall(".//zoomLevel")
for node in levels:
if float(node.find("level").text) == zoom:
self.micronsPerXPixel = float(node.find("micronsPerXPixel").text)
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.zoom_params_file} for zoom level {zoom}.
"""
)

def get_beam_position_from_zoom(self, zoom: float) -> Tuple[int, int]:
"""
Extracts the beam location in pixels `xCentre` `yCentre`, for a requested zoom \
level. The beam location is manually inputted by the beamline operator on GDA \
by clicking where on screen a scintillator lights up, and stored in the \
display.configuration file.
"""
crosshair_x_line = None
crosshair_y_line = None
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(zoom)):
crosshair_x_line = file_lines[i + 1]
crosshair_y_line = file_lines[i + 2]
break

if crosshair_x_line is None or crosshair_y_line is None:
raise OAVError_BeamPositionNotFound(
f"Could not extract beam position at zoom level {zoom}"
)

beam_centre_i = int(crosshair_x_line.split(" = ")[1])
beam_centre_j = int(crosshair_y_line.split(" = ")[1])
LOGGER.info(f"Beam centre: {beam_centre_i, beam_centre_j}")
return beam_centre_i, beam_centre_j

def calculate_beam_distance(
self, horizontal_pixels: int, vertical_pixels: int
) -> Tuple[int, int]:
"""
Calculates the distance between the beam centre and the given (horizontal, vertical).
Args:
horizontal_pixels (int): The x (camera coordinates) value in pixels.
vertical_pixels (int): The y (camera coordinates) value in pixels.
Returns:
The distance between the beam centre and the (horizontal, vertical) point in pixels as a tuple
(horizontal_distance, vertical_distance).
"""

return (
self.beam_centre_i - horizontal_pixels,
self.beam_centre_j - vertical_pixels,
)


class OAV(AreaDetector):
cam: CamBase = ADC(CamBase, "-DI-OAV-01:CAM:")
roi: ROIPlugin = ADC(ROIPlugin, "-DI-OAV-01:ROI:")
Expand All @@ -80,3 +176,8 @@ class OAV(AreaDetector):
snapshot: SnapshotWithGrid = Component(SnapshotWithGrid, "-DI-OAV-01:MJPG:")
mxsc: MXSC = ADC(MXSC, "-DI-OAV-01:MXSC:")
zoom_controller: ZoomController = Component(ZoomController, "-EA-OAV-01:FZOOM:")

def __init__(self, *args, params: OAVConfigParams, **kwargs):
super().__init__(*args, **kwargs)
self.parameters = params
self.zoom_controller.level.subscribe(self.parameters.update_on_zoom)
99 changes: 10 additions & 89 deletions src/dodal/devices/oav/oav_parameters.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,30 @@
import json
import xml.etree.cElementTree as et
from collections import ChainMap
from typing import Any, Tuple
from typing import Any

from dodal.devices.oav.oav_errors import (
OAVError_BeamPositionNotFound,
OAVError_ZoomLevelNotFound,
OAV_CONFIG_JSON = (
"/dls_sw/i03/software/daq_configuration/json/OAVCentring_hyperion.json"
)
from dodal.log import LOGGER

OAV_CONFIG_FILE_DEFAULTS = {
"zoom_params_file": "/dls_sw/i03/software/gda/configurations/i03-config/xml/jCameraManZoomLevels.xml",
"oav_config_json": "/dls_sw/i03/software/daq_configuration/json/OAVCentring_hyperion.json",
"display_config": "/dls_sw/i03/software/gda_versions/var/display.configuration",
}


class OAVParameters:
# The zoom level as a float e.g. 2.0
zoom: float
"""
The parameters to set up the OAV depending on the context.
"""

def __init__(
self,
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"],
oav_config_json=OAV_CONFIG_JSON,
):
self.zoom_params_file: str = zoom_params_file
self.oav_config_json: str = oav_config_json
self.display_config: str = display_config
self.context = context

self.global_params, self.context_dicts = self.load_json(self.oav_config_json)
self.active_params: ChainMap = ChainMap(
self.context_dicts[self.context], self.global_params
)
self.update_self_from_current_context()
self.load_microns_per_pixel()
self.beam_centre_i, self.beam_centre_j = self.get_beam_position_from_zoom()

@staticmethod
def load_json(filename: str) -> tuple[dict[str, Any], dict[str, dict]]:
Expand Down Expand Up @@ -95,73 +81,8 @@ def update(name, param_type, default=None):
self.direction: int = update("direction", int)
self.max_tip_distance: float = update("max_tip_distance", float, default=300)

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 hyperion could generate them in future.
"""
if not zoom:
zoom = self.zoom

tree = et.parse(self.zoom_params_file)
self.micronsPerXPixel = self.micronsPerYPixel = None
root = tree.getroot()
levels = root.findall(".//zoomLevel")
for node in levels:
if float(node.find("level").text) == zoom:
self.micronsPerXPixel = float(node.find("micronsPerXPixel").text)
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.zoom_params_file} for zoom level {zoom}."
)

# get the max tip distance in pixels
self.max_tip_distance_pixels = self.max_tip_distance / self.micronsPerXPixel

def get_beam_position_from_zoom(self, zoom: float = None) -> Tuple[int, int]:
def get_max_tip_distance_in_pixels(self, micronsPerPixel: float) -> float:
"""
Extracts the beam location in pixels `xCentre` `yCentre`, for a requested zoom \
level. The beam location is manually inputted by the beamline operator on GDA \
by clicking where on screen a scintillator ligths up and stored in the \
display.configuration file.
Get the maximum tip distance in pixels.
"""
if not zoom:
zoom = self.zoom

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(zoom)):
crosshair_x_line = file_lines[i + 1]
crosshair_y_line = file_lines[i + 2]
break

if crosshair_x_line is None or crosshair_y_line is None:
raise OAVError_BeamPositionNotFound(
f"Could not extract beam position at zoom level {zoom}"
)

beam_centre_i = int(crosshair_x_line.split(" = ")[1])
beam_centre_j = int(crosshair_y_line.split(" = ")[1])
LOGGER.info(f"Beam centre: {beam_centre_i, beam_centre_j}")
return beam_centre_i, beam_centre_j

def calculate_beam_distance(
self, horizontal_pixels: int, vertical_pixels: int
) -> Tuple[int, int]:
"""
Calculates the distance between the beam centre and the given (horizontal, vertical).
Args:
horizontal_pixels (int): The x (camera coordinates) value in pixels.
vertical_pixels (int): The y (camera coordinates) value in pixels.
Returns:
The distance between the beam centre and the (horizontal, vertical) point in pixels as a tuple
(horizontal_distance, vertical_distance).
"""

return (
self.beam_centre_i - horizontal_pixels,
self.beam_centre_j - vertical_pixels,
)
return self.max_tip_distance / micronsPerPixel
Loading

0 comments on commit 3b1557e

Please sign in to comment.