diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py index ed075b5ebb..3d7d76d2c1 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py @@ -1,142 +1,30 @@ -import time - -from gevent.event import AsyncResult - from mxcubecore import HardwareRepository as HWR -from mxcubecore.HardwareObjects.GenericDiffractometer import ( - GenericDiffractometer, - PhaseEnum, +from mxcubecore.HardwareObjects.abstract.AbstractDiffractometer import ( + AbstractDiffractometer, + DiffractometerPhase, ) -class LNLSDiffractometer(GenericDiffractometer): - def __init__(self, name): - GenericDiffractometer.__init__(self, name) - +class LNLSDiffractometer(AbstractDiffractometer): def init(self): - GenericDiffractometer.init(self) + AbstractDiffractometer.init(self) self._bluesky_api = HWR.beamline.get_object_by_role("bluesky") - self.pixels_per_mm_x = 10**-4 - self.pixels_per_mm_y = 10**-4 - self.beam_position = [318, 238] - self.in_plate_mode = False - self.last_centred_position = self.beam_position - self.current_motor_positions = { - "phiy": 0, - "sampx": 0, - "sampy": 0, - "zoom": 0, - "focus": 0, - "phiz": 0, - "phi": 0, - "kappa": 0, - "kappa_phi": 0, - } - - self.centring_time = 0 - self.mount_mode = self.get_property("sample_mount_mode") - if self.mount_mode is None: - self.mount_mode = "manual" - - self.connect(self.motor_hwobj_dict["phi"], "valueChanged", self.phi_motor_moved) - self.connect( - self.motor_hwobj_dict["phiy"], "valueChanged", self.phiy_motor_moved - ) - self.connect( - self.motor_hwobj_dict["phiz"], "valueChanged", self.phiz_motor_moved - ) - self.connect( - self.motor_hwobj_dict["kappa"], "valueChanged", self.kappa_motor_moved - ) - self.connect( - self.motor_hwobj_dict["kappa_phi"], - "valueChanged", - self.kappa_phi_motor_moved, - ) - self.connect( - self.motor_hwobj_dict["sampx"], "valueChanged", self.sampx_motor_moved - ) - self.connect( - self.motor_hwobj_dict["sampy"], "valueChanged", self.sampy_motor_moved - ) - - def is_ready(self) -> bool: + self.current_phase = DiffractometerPhase.UNKNOWN + self.update_state(self.STATES.READY) + self.connect(self.sampx, "valueChanged", self.sampx.update_grid_value) + self.connect(self.sampy, "valueChanged", self.sampy.update_grid_value) + + def get_pixels_per_mm(self): + zoom_enum = self.zoom.get_value() + current_zoom = zoom_enum.name + mm_per_pixel_x = self.zoom.get_property("mm_per_pixel_x")[current_zoom] + mm_per_pixel_y = self.zoom.get_property("mm_per_pixel_y")[current_zoom] + pixel_per_mm_x = round(1 / mm_per_pixel_x, 6) + pixel_per_mm_y = round(1 / mm_per_pixel_y, 6) + return (pixel_per_mm_x, pixel_per_mm_y) + + def save_centring_positions(self): + self.update_state(self.STATES.READY) + + def wait_status_ready(self, timeout=None): return True - - def phi_motor_moved(self, pos): - self.current_motor_positions["phi"] = pos - self.emit("phiMotorMoved", pos) - - def phiy_motor_moved(self, pos): - self.current_motor_positions["phiy"] = pos - - def phiz_motor_moved(self, pos): - self.current_motor_positions["phiz"] = pos - - def sampx_motor_moved(self, pos): - self.current_motor_positions["sampx"] = pos - - def sampy_motor_moved(self, pos): - self.current_motor_positions["sampy"] = pos - - def kappa_motor_moved(self, pos): - self.current_motor_positions["kappa"] = pos - if time.time() - self.centring_time > 1.0: - self.invalidate_centring() - self.emit_diffractometer_moved() - self.emit("kappaMotorMoved", pos) - - def kappa_phi_motor_moved(self, pos): - self.current_motor_positions["kappa_phi"] = pos - if time.time() - self.centring_time > 1.0: - self.invalidate_centring() - self.emit_diffractometer_moved() - self.emit("kappaPhiMotorMoved", pos) - - def manual_centring(self): - self.log.info("Initializing manual sample alignment...") - for step in range(3): - self.log.info(f"Step {step + 1} of 3...") - self.user_clicked_event = AsyncResult() - self.waiting_for_click = True - x, y = self.user_clicked_event.get() - self.log.info(f"{x}, {y}") - self._bluesky_api.execute_plan( - plan_name="manual_alignment", - kwargs={"x_px": x, "y_px": y, "step": step}, - ) - self.log.info("Manual sample alignment has finished...") - return {} - - def automatic_centring(self): - self.log.info("Initializing automatic sample alignment...") - self._bluesky_api.execute_plan(plan_name="automatic_alignment") - self.log.info("Automatic sample alignment has finished...") - - def move_to_beam(self, x, y, omega=None): - self.log.info("Moving to beam...") - - self._bluesky_api.execute_plan( - plan_name="move_to_beam", - kwargs={ - "x_px": x - self.beam_position[0], - "y_px": y - self.beam_position[1], - }, - ) - self.log.info("Move to beam has finished...") - - def motor_positions_to_screen(self, motor_positions): - return self.beam_position - - def get_value_motors(self): - return self.current_motor_positions - - def get_phase(self): - unknown_phase = PhaseEnum.unknown - phase = self.get_current_phase() - if not phase: - phase = unknown_phase - return phase - - def get_chip_configuration(self): - return None diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py new file mode 100644 index 0000000000..67e8578fc0 --- /dev/null +++ b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py @@ -0,0 +1,172 @@ +import logging + +import gevent +from mxcubeweb.app import MXCUBEApplication as frontendApplication +from mxcubeweb.core.util.convertutils import to_camel + +from mxcubecore import HardwareRepository as HWR +from mxcubecore.HardwareObjects.abstract.AbstractSampleChanger import SampleChangerState +from mxcubecore.HardwareObjects.SampleView import Grid, SampleView + + +class LNLSSampleView(SampleView): + def init(self): + SampleView.init(self) + self.user_level_log = logging.getLogger("user_level_log") + self._bluesky_api = HWR.beamline.get_object_by_role("bluesky") + self.sc = HWR.beamline.get_object_by_role("sample_changer") + self.READY_FOR_NEXT_CLICK = gevent.event.Event() + self.x, self.y = None, None + self.frontend_application = frontendApplication + self.current_centring_method = None + + def move_to_beam_bluesky(self, x, y, plan_name, step=-1): + beam_pos = HWR.beamline.beam.get_beam_position_on_screen() + plan_kwargs = { + "x_px": beam_pos[0] - x, + "y_px": y - beam_pos[1], + } + if step != -1: + plan_kwargs["step"] = step + self._bluesky_api.execute_plan(plan_name=plan_name, kwargs=plan_kwargs) + + def move_to_beam(self, x, y): + if self.sc.get_state() != SampleChangerState.Ready: + return + self.user_level_log.info("Moving to beam...") + self.move_to_beam_bluesky(x, y, "move_to_beam") + self.user_level_log.info("Move to beam has finished...") + + def start_centring(self, centring_method): + self.current_centring_method = centring_method + self.current_centring_procedure = centring_method + self.emit("centringStarted", (centring_method)) + + def finish_centring(self): + self.centring_status["valid"] = True + omega, phiy, phiz, sampx, sampy = self.get_current_diffractometer_positions() + self.centring_status["motors"] = { + "omega": omega, + "phiy": phiy, + "phiz": phiz, + "sampx": sampx, + "sampy": sampy, + } + self.emit( + "centringSuccessful", + (self.current_centring_method, self.get_centring_status()), + ) + if self.current_centring_method == "Manual": + self.shapes.clear() + self.frontend_application.server.emit( + "update_shapes", {"shapes": self.shapes}, namespace="/hwr" + ) + self.frontend_application.server.emit("abort_centring", namespace="/hwr") + self.current_centring_procedure = None + self.current_centring_method = None + + def start_auto_centring(self): + self.user_level_log.info("Initializing automatic sample alignment...") + if self.current_centring_method is not None: + self.user_level_log.info("Already centring") + return + self.start_centring("Automatic") + self._bluesky_api.execute_plan(plan_name="automatic_alignment") + self.user_level_log.info("Automatic sample alignment has finished...") + self.finish_centring() + + def image_clicked(self, x, y): + logging.getLogger("user_level_log").info( + f"LNLS Centring click at x:{int(x)}, y:{int(y)}" + ) + self.x = x + self.y = y + self.READY_FOR_NEXT_CLICK.set() + + def start_manual_centring(self, nb_click: int = 3): + if self.sc.get_state() != SampleChangerState.Ready: + return + self.user_level_log.info("Initializing manual sample alignment...") + if self.current_centring_method is not None: + self.user_level_log.info("Already centring") + return + self.start_centring("Manual") + for step in range(3): + self.READY_FOR_NEXT_CLICK.clear() + self.READY_FOR_NEXT_CLICK.wait() + if (self.x is not None) and (self.y is not None): + self.move_to_beam_bluesky(self.x, self.y, "manual_alignment", step) + self.x = None + self.y = None + self.user_level_log.info("Manual sample alignment has finished...") + self.finish_centring() + + def get_snapshot(self): + return None + + def _wait_for_centring_finishes(self): + return + + def get_current_diffractometer_positions(self): + d = HWR.beamline.diffractometer + omega = d.omega.get_value() + phiy = d.phiy.get_value() + phiz = d.phiz.get_value() + sampx = d.sampx.get_value() + sampy = d.sampy.get_value() + return omega, phiy, phiz, sampx, sampy + + def get_current_mm_per_pixel(self): + d = HWR.beamline.diffractometer + zoom_enum = d.zoom.get_value() + current_zoom = zoom_enum.name + mm_per_pixel_x = d.zoom.get_property("mm_per_pixel_x")[current_zoom] + mm_per_pixel_y = d.zoom.get_property("mm_per_pixel_y")[current_zoom] + return mm_per_pixel_x, mm_per_pixel_y + + def get_centred_point_from_coord(self, x, y, return_by_names=None): + omega, phiy, phiz, sampx, sampy = self.get_current_diffractometer_positions() + + beam_pos = HWR.beamline.beam.get_beam_position_on_screen() + x_px = beam_pos[0] - x + y_px = y - beam_pos[1] + + mm_per_pixel_x, mm_per_pixel_y = self.get_current_mm_per_pixel() + + sampx = sampx + x_px * mm_per_pixel_x + sampy = sampy + y_px * mm_per_pixel_y + + return { + "omega": omega, + "phiy": phiy, + "phiz": phiz, + "sampx": sampx, + "sampy": sampy, + } + + def _update_shape_positions(self, *args, **kwargs): + for shape in self.get_shapes(): + if not isinstance(shape, Grid): + shape.update_position(self.motor_positions_to_screen) + self.emit("shapesChanged") + + def update_grid_positions(self, pixel_diff_x, pixel_diff_y): + final_shape_dict = {} + grid_list = self.get_grids() + for grid in grid_list: + shape = self.get_shape(grid.id) + shape_dict = to_camel(shape.as_dict()) + previous_coord = shape_dict["screenCoord"] + new_coord = [ + (previous_coord[0] - pixel_diff_x), + previous_coord[1] - pixel_diff_y, + ] + shape_dict["screenCoord"] = new_coord + shape_dict["cellCountFun"] = "left-to-right" + grid.update_from_dict({"screenCoord": new_coord}) + grid.screen_coord = new_coord + final_shape_dict.update({grid.id: shape_dict}) + self.frontend_application.server.emit( + "update_shapes", {"shapes": final_shape_dict}, namespace="/hwr" + ) + self.emit("shapesChanged")