Source code for mxcubecore.HardwareObjects.mockup.BeamMockup

# encoding: utf-8
#
#  Project name: MXCuBE
#  https://github.com/mxcube
#
#  This file is part of MXCuBE software.
#
#  MXCuBE is free software: you can redistribute it and/or modify
#  it under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  MXCuBE is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU Lesser General Public License for more details.
#
#  You should have received a copy of the GNU Lesser General Public License
#  along with MXCuBE. If not, see <http://www.gnu.org/licenses/>.

"""
BeamMockup class - methods to define the size and shape of he beam.

Example xml configuration:

.. code-block:: xml

  <object class="BeamMockup">
    <object href="/beam_definer" role="definer"/>
    <!-- accepted definer_type values: aperture, slits, definer -->
    <definer_type>definer</definer_type>
    <beam_divergence_vertical>0</beam_divergence_vertical>
    <beam_divergence_horizontal>0</beam_divergence_horizontal>
    <check_beam>(0.03, 0.03)</check_beam>
  </object>
"""

__copyright__ = """ Copyright © by MXCuBE Collaboration """
__license__ = "LGPLv3+"

from ast import literal_eval

from mxcubecore.HardwareObjects.abstract.AbstractBeam import AbstractBeam, BeamShape


[docs]class BeamMockup(AbstractBeam): """Beam Mockup class""" def __init__(self, name): super().__init__(name) self._definer_type = None self._check_beam = ()
[docs] def init(self): """Initialize hardware""" super().init() # # backward compatibility hack to support loading from XML config file # # when loading from YAML configuration file, # the attributes will be automatically set to the specified child HWOBJs # # when loading from XML, it does not happen, so fall back to # get_object_by_role() # _definer_type = None self._aperture = self.get_object_by_role("aperture") self._slits = self.get_object_by_role("slits") self._definer = self.get_object_by_role("definer") if self.aperture: _definer_type = "aperture" self.aperture.connect("valueChanged", self.aperture_diameter_changed) if self.slits: _definer_type = "slits" self.slits.connect("valueChanged", self.slits_gap_changed) if self.definer: _definer_type = "definer" self.definer.connect("valueChanged", self._re_emit_values) self._definer_type = self.get_property("definer_type", _definer_type) self._beam_position_on_screen = self.get_property("beam_position") or [318, 238] if isinstance(self._beam_position_on_screen, str): self._beam_position_on_screen = literal_eval(self._beam_position_on_screen) self._check_beam = self.get_property("check_beam") or False if isinstance(self._check_beam, str): self._check_beam = literal_eval(self._check_beam) # Needed to trigger first value setting self.get_value() self.re_emit_values() self.emit("beamPosChanged", (self._beam_position_on_screen,))
def _re_emit_values(self, *args, **kwargs): self.re_emit_values() def _get_aperture_value(self) -> tuple[list[float, float], str]: """Get the size and the label of the aperture in place. Returns: Size [mm] (width, height), label. """ _size = self.aperture.get_value().value[0] try: _label = self.aperture.get_value().name except AttributeError: _label = str(_size) _size /= 1000.0 return [_size, _size], _label def _get_definer_value(self) -> tuple[list[float, float], str]: """Get the size and the name of the definer in place. Returns: Size [mm] (width, height), label. """ try: value = self.definer.get_value() if isinstance(value, tuple): return [value[1], value[1]], value[0] return list(value.value), value.name except AttributeError: return [-1, -1], "UNKNOWN" def _get_slits_value(self) -> tuple[list[float, float], str]: """Get the size of the slits in place. Returns: Size [mm] (width, height), label. """ _size = self.slits.get_gaps() return _size, "slits" def _get_value(self) -> tuple[float, float, BeamShape, str]: """Get the size (width and height) of the beam, its shape and its label. The size is in mm. Returns: Four-item tuple: width, height, shape, name """ labels = {} _label = "UNKNOWN" if self.aperture: _size, _name = self._get_aperture_value() self._beam_size_dict.update({"aperture": _size}) labels.update({"aperture": _name}) if self.slits: _size, _name = self._get_slits_value() self._beam_size_dict.update({"slits": _size}) labels.update({"slits": _name}) if self.definer: _size, _name = self._get_definer_value() self._beam_size_dict.update({"definer": _size}) labels.update({"definer": _name}) info_dict = self.evaluate_beam_info() try: _label = labels[info_dict["label"]] self._beam_info_dict["label"] = _label except KeyError: _label = info_dict["label"] return self._beam_width, self._beam_height, self._beam_shape, _label
[docs] def aperture_diameter_changed(self, aperture): """Method called when the aperture diameter changes. Args: Aperture enum. """ size = aperture.value[0] self.aperture.update_value(aperture) self._beam_size_dict["aperture"] = [size, size] self.evaluate_beam_info() self._beam_info_dict["label"] = aperture.name self.re_emit_values()
[docs] def slits_gap_changed(self, size: tuple[float, float]): """Method called when the slits gap changes. Args: Two floats - beam size in microns """ self._beam_size_dict["slits"] = size self._beam_info_dict["label"] = "slits" self.evaluate_beam_info() self.re_emit_values()
[docs] def set_beam_position_on_screen(self, beam_x_y: list[int, int]): """Sets beam mark position on screen. #TODO move method to sample_view Args: Position [x, y] in pixels. """ self._beam_position_on_screen = beam_x_y self.emit("beamPosChanged", (self._beam_position_on_screen,))
[docs] def get_slits_gap(self) -> tuple[float, float]: """Get the beam size from the slits gap. Returns: Two-item tuple with horizontal and vertical beam size in microns """ self.evaluate_beam_info() return self._beam_size_dict["slits"]
[docs] def set_slits_gap(self, width_microns: int, height_microns: int): """Sets slits gap in microns. Args: width and height in microns. """ if self.slits: self.slits.set_horizontal_gap(width_microns / 1000.0) self.slits.set_vertical_gap(height_microns / 1000.0)
[docs] def get_aperture_pos_name(self) -> str: """Get the name of the current aperture. Returns: name of current aperture position """ return self.aperture.get_current_pos_name()
[docs] def get_defined_beam_size(self) -> dict: """Get the predefined beam labels and size. Returns: Dictionary with lists of available beam size labels and the corresponding size (width,height) tuples. ``{"label": [str, str, ...], "size": [(w,h), (w,h), ...]}`` """ labels = [] values = [] if self._definer_type == "slits": return { "label": ["low", "high"], "size": [self.slits.get_min_limits(), self.slits.get_max_limits()], } if self._definer_type == "aperture": _enum = self.aperture.VALUES elif self._definer_type == "definer": _enum = self.definer.VALUES for value in _enum: _nam = value.name if _nam not in ["IN", "OUT", "UNKNOWN"]: labels.append(_nam) if self._definer_type == "aperture": values.append((value.value[0] / 1000.0, value.value[0] / 1000.0)) else: values.append(value.value) return {"label": labels, "size": values}
[docs] def get_available_size(self) -> dict: """Get the available predefined beam definer configuration. Returns: ``{"type": ["aperture"], "values": [labels]}`` or ``{"type": ["definer"], "values": [labels]}`` or ``{"type": ["width", "height"], "values": [low_lim_w, high_lim_w, low_lim_h, high_lim_h]}`` """ if self._definer_type == "aperture": # get list of the available apertures return { "type": ["aperture"], "values": self.aperture.get_diameter_size_list(), } if self._definer_type == "definer": # get list of the available definer positions return { "type": ["definer"], "values": self.definer.get_predefined_positions_list(), } if self._definer_type == "slits": # get the list of the slits motors range _low_w, _low_h = self.slits.get_min_limits() _high_w, _high_h = self.slits.get_max_limits() return { "type": ["width", "height"], "values": [_low_w, _high_w, _low_h, _high_h], } return {}
[docs] def set_value(self, size: list[float, float] | str | None = None): """Set the beam size. Args: size: List of width and height in micrometers or Aperture or definer definer name as string. Raises: RuntimeError: Beam definer not configured Size out of the limits. TypeError: Wrong size type. """ msg = "Incorrect input value for " if self._definer_type in (self.slits, "slits"): if not isinstance(size, list): msg += "slits" raise TypeError(msg) self.slits.set_horizontal_gap(size[0]) self.slits.set_vertical_gap(size[1]) if self._definer_type in (self.aperture, "aperture"): if not isinstance(size, str): msg += "aperture" raise TypeError(msg) self.aperture.set_value(self.aperture.VALUES[size], timeout=2) if self._definer_type in (self.definer, "definer"): if not isinstance(size, str): msg += "definer" raise TypeError(msg) self.definer.set_value(self.definer.VALUES[size], timeout=2)
def _is_beam(self) -> bool: """Check if there is beam. Returns: ``True`` if beam present, ``False`` otherwise """ if not self._check_beam: return True beam = self.get_value() return all( x1 <= x2 for (x1, x2) in zip(self._check_beam, (beam[0], beam[1]), strict=False) )