From b32d867dc7db0ad3b779c14bc913b1d385785702 Mon Sep 17 00:00:00 2001 From: "rafael.lyra" Date: Mon, 13 Jul 2026 09:47:02 -0300 Subject: [PATCH 1/9] Remove unused methods --- .../LNLS/LNLSDiffractometer.py | 54 +------------------ 1 file changed, 1 insertion(+), 53 deletions(-) diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py index ed075b5ebb..651f4aa6d0 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py @@ -28,7 +28,7 @@ def init(self): "zoom": 0, "focus": 0, "phiz": 0, - "phi": 0, + "omega": 0, "kappa": 0, "kappa_phi": 0, } @@ -38,61 +38,9 @@ def init(self): 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: 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): From 58bf62a622e9cd6a835eb8d98b71ecb184ea7df6 Mon Sep 17 00:00:00 2001 From: "rafael.lyra" Date: Mon, 13 Jul 2026 09:47:02 -0300 Subject: [PATCH 2/9] Use AbstractDiffractometer in LNLSDiffractometer class --- .../HardwareObjects/LNLS/LNLSDiffractometer.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py index 651f4aa6d0..cfd10d18a7 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py @@ -1,20 +1,13 @@ -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 @@ -80,7 +73,7 @@ def get_value_motors(self): return self.current_motor_positions def get_phase(self): - unknown_phase = PhaseEnum.unknown + unknown_phase = DiffractometerPhase.UNKNOWN phase = self.get_current_phase() if not phase: phase = unknown_phase From 9f6c8475a9ed0a25bdb1dd8de2060fe99bfed857 Mon Sep 17 00:00:00 2001 From: "rafael.lyra" Date: Mon, 13 Jul 2026 09:47:02 -0300 Subject: [PATCH 3/9] Simplify LNLSDiffractometer --- .../LNLS/LNLSDiffractometer.py | 45 +++---------------- 1 file changed, 5 insertions(+), 40 deletions(-) diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py index cfd10d18a7..41677d5a14 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py @@ -9,30 +9,8 @@ class LNLSDiffractometer(AbstractDiffractometer): def 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, - "omega": 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" - - def is_ready(self) -> bool: - return True + self.current_motor_positions = {} + self.current_phase = DiffractometerPhase.UNKNOWN def manual_centring(self): self.log.info("Initializing manual sample alignment...") @@ -65,19 +43,6 @@ def move_to_beam(self, x, y, omega=None): }, ) 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 = DiffractometerPhase.UNKNOWN - phase = self.get_current_phase() - if not phase: - phase = unknown_phase - return phase - - def get_chip_configuration(self): - return None + + def get_pixels_per_mm(self): + return (1, 1) \ No newline at end of file From 89762bac4ae56818c28b95a7ffd67eeed9e8f93d Mon Sep 17 00:00:00 2001 From: "rafael.lyra" Date: Mon, 13 Jul 2026 09:47:02 -0300 Subject: [PATCH 4/9] Move some methods to LNLSSampleView --- .../LNLS/LNLSDiffractometer.py | 47 ++++--------------- .../HardwareObjects/LNLS/LNLSSampleView.py | 45 ++++++++++++++++++ 2 files changed, 55 insertions(+), 37 deletions(-) create mode 100644 mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py index 41677d5a14..c33aba669b 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py @@ -1,48 +1,21 @@ -from gevent.event import AsyncResult - from mxcubecore import HardwareRepository as HWR -from mxcubecore.HardwareObjects.abstract.AbstractDiffractometer import AbstractDiffractometer, DiffractometerPhase +from mxcubecore.HardwareObjects.abstract.AbstractDiffractometer import ( + AbstractDiffractometer, + DiffractometerPhase, +) class LNLSDiffractometer(AbstractDiffractometer): - def init(self): AbstractDiffractometer.init(self) self._bluesky_api = HWR.beamline.get_object_by_role("bluesky") - self.current_motor_positions = {} self.current_phase = DiffractometerPhase.UNKNOWN - 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 get_pixels_per_mm(self): + return (1, 1) - def move_to_beam(self, x, y, omega=None): - self.log.info("Moving to beam...") + def save_centring_positions(self): + return - 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 get_pixels_per_mm(self): - return (1, 1) \ No newline at end of file + def wait_status_ready(self, timeout=None): + return True \ No newline at end of file diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py new file mode 100644 index 0000000000..cf937a2e71 --- /dev/null +++ b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py @@ -0,0 +1,45 @@ +import logging +from gevent.event import AsyncResult + +from mxcubecore import HardwareRepository as HWR +from mxcubecore.HardwareObjects.SampleView import 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.update_state(self.STATES.READY) + + def move_to_beam(self, x, y, omega=None): + self.user_level_log.info("Moving to beam...") + + beam_pos = HWR.beamline.beam.get_beam_position_on_screen() + self._bluesky_api.execute_plan( + plan_name="move_to_beam", + kwargs={ + "x_px": x - beam_pos[0], + "y_px": y - beam_pos[1], + }, + ) + self.user_level_log.info("Move to beam has finished...") + + def start_manual_centring(self, nb_click: int = 3): + self.user_level_log.info("Initializing manual sample alignment...") + for step in range(nb_click): + self.user_level_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._bluesky_api.execute_plan( + plan_name="manual_alignment", + kwargs={"x_px": x, "y_px": y, "step": step}, + ) + self.user_level_log.info("Manual sample alignment has finished...") + + def start_auto_centring(self): + self.user_level_log.info("Initializing automatic sample alignment...") + self._bluesky_api.execute_plan(plan_name="automatic_alignment") + self.user_level_log.info("Automatic sample alignment has finished...") From 2ea4e1e40d8313fb02476b3f6cae29c078f0fb0b Mon Sep 17 00:00:00 2001 From: "rafael.lyra" Date: Mon, 13 Jul 2026 09:47:02 -0300 Subject: [PATCH 5/9] Set LNLSDiffractometer state as ready --- mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py | 1 + mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py index c33aba669b..c27d3360f9 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py @@ -10,6 +10,7 @@ def init(self): AbstractDiffractometer.init(self) self._bluesky_api = HWR.beamline.get_object_by_role("bluesky") self.current_phase = DiffractometerPhase.UNKNOWN + self.set_is_ready(True) def get_pixels_per_mm(self): return (1, 1) diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py index cf937a2e71..5937093215 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py @@ -11,7 +11,6 @@ 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.update_state(self.STATES.READY) def move_to_beam(self, x, y, omega=None): self.user_level_log.info("Moving to beam...") @@ -31,7 +30,6 @@ def start_manual_centring(self, nb_click: int = 3): for step in range(nb_click): self.user_level_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._bluesky_api.execute_plan( plan_name="manual_alignment", From 62951e1e96ef6338c943a27cc486a290247ebf37 Mon Sep 17 00:00:00 2001 From: "rafael.lyra" Date: Mon, 13 Jul 2026 09:47:02 -0300 Subject: [PATCH 6/9] Fix pre-commit --- mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py | 2 +- mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py index c27d3360f9..82aac5f6b6 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py @@ -19,4 +19,4 @@ def save_centring_positions(self): return def wait_status_ready(self, timeout=None): - return True \ No newline at end of file + return True diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py index 5937093215..d63d1bbbd0 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py @@ -1,4 +1,5 @@ import logging + from gevent.event import AsyncResult from mxcubecore import HardwareRepository as HWR @@ -6,13 +7,12 @@ 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") - def move_to_beam(self, x, y, omega=None): + def move_to_beam(self, x, y): self.user_level_log.info("Moving to beam...") beam_pos = HWR.beamline.beam.get_beam_position_on_screen() From d59cc8fc3dca79fe9d62d10a1a455dad7bea430f Mon Sep 17 00:00:00 2001 From: Pedro Benetton Date: Mon, 13 Jul 2026 09:47:02 -0300 Subject: [PATCH 7/9] Add get_pixels_per_mm at LNLSDiffractometer --- mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py index 82aac5f6b6..be7ed91788 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py @@ -10,10 +10,16 @@ def init(self): AbstractDiffractometer.init(self) self._bluesky_api = HWR.beamline.get_object_by_role("bluesky") self.current_phase = DiffractometerPhase.UNKNOWN - self.set_is_ready(True) + self.update_state(self.STATES.READY) def get_pixels_per_mm(self): - return (1, 1) + 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): return From e47a5b69f9ed7214a4b036783855085c6cff7c21 Mon Sep 17 00:00:00 2001 From: Pedro Benetton Date: Mon, 13 Jul 2026 09:47:02 -0300 Subject: [PATCH 8/9] Adjust 3-click centring, autocentring and move to beam at LNLSSampleView --- .../HardwareObjects/LNLS/LNLSSampleView.py | 68 ++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py index d63d1bbbd0..6f31d3e2a1 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py @@ -1,8 +1,10 @@ import logging -from gevent.event import AsyncResult +import gevent +from mxcubeweb.app import MXCUBEApplication as frontendApplication from mxcubecore import HardwareRepository as HWR +from mxcubecore.HardwareObjects.abstract.AbstractSampleChanger import SampleChangerState from mxcubecore.HardwareObjects.SampleView import SampleView @@ -11,33 +13,81 @@ 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 def move_to_beam(self, x, y): + if self.sc.get_state() != SampleChangerState.Ready: + return self.user_level_log.info("Moving to beam...") beam_pos = HWR.beamline.beam.get_beam_position_on_screen() self._bluesky_api.execute_plan( plan_name="move_to_beam", kwargs={ - "x_px": x - beam_pos[0], + "x_px": beam_pos[0] - x, "y_px": y - beam_pos[1], }, ) self.user_level_log.info("Move to beam has finished...") + 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_procedure is not None: + self.user_level_log.exception("Already centring") + self.current_centring_procedure = "Manual" + self.emit("centringStarted", ("Manual")) for step in range(nb_click): - self.user_level_log.info(f"Step {step + 1} of 3...") - self.user_clicked_event = AsyncResult() - x, y = self.user_clicked_event.get() - self._bluesky_api.execute_plan( - plan_name="manual_alignment", - kwargs={"x_px": x, "y_px": y, "step": step}, - ) + self.READY_FOR_NEXT_CLICK.clear() + self.READY_FOR_NEXT_CLICK.wait() + beam_pos = HWR.beamline.beam.get_beam_position_on_screen() + if (self.x is not None) and (self.y is not None): + self._bluesky_api.execute_plan( + plan_name="manual_alignment", + kwargs={ + "x_px": beam_pos[0] - self.x, + "y_px": self.y - beam_pos[1], + "step": step, + }, + ) self.user_level_log.info("Manual sample alignment has finished...") + self.centring_done() + self.accept_centring() + self.emit("centringSuccessful", ("Manual", self.get_centring_status())) + # Clear all shapes at frontend when 3-click centring finishes + self.shapes.clear() + self.frontend_application.server.emit( + "update_shapes", {"shapes": self.shapes}, namespace="/hwr" + ) def start_auto_centring(self): self.user_level_log.info("Initializing automatic sample alignment...") + if self.current_centring_procedure is not None: + self.user_level_log.exception("Already centring") + self.current_centring_procedure = "Automatic" + self.emit("centringStarted", ("Automatic")) self._bluesky_api.execute_plan(plan_name="automatic_alignment") self.user_level_log.info("Automatic sample alignment has finished...") + self.centring_done() + self.accept_centring() + + def get_snapshot(self): + return None + + def _wait_for_centring_finishes(self): + return + + def get_centred_point_from_coord(self, x, y, return_by_names=None): # noqa: ARG002 + return {"omega": 0, "phiy": 0, "phiz": 0, "sampx": 0, "sampy": 0} From 6b346328d8983c68939d80d9ce186baa0842dedd Mon Sep 17 00:00:00 2001 From: Pedro Benetton Date: Mon, 13 Jul 2026 09:47:02 -0300 Subject: [PATCH 9/9] Update LNLSSampleView and LNLSDiffractometer --- .../LNLS/LNLSDiffractometer.py | 4 +- .../HardwareObjects/LNLS/LNLSSampleView.py | 167 +++++++++++++----- 2 files changed, 126 insertions(+), 45 deletions(-) diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py index be7ed91788..3d7d76d2c1 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSDiffractometer.py @@ -11,6 +11,8 @@ def init(self): self._bluesky_api = HWR.beamline.get_object_by_role("bluesky") 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() @@ -22,7 +24,7 @@ def get_pixels_per_mm(self): return (pixel_per_mm_x, pixel_per_mm_y) def save_centring_positions(self): - return + self.update_state(self.STATES.READY) def wait_status_ready(self, timeout=None): return True diff --git a/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py index 6f31d3e2a1..67e8578fc0 100644 --- a/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py +++ b/mxcubecore/HardwareObjects/LNLS/LNLSSampleView.py @@ -2,10 +2,11 @@ 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 SampleView +from mxcubecore.HardwareObjects.SampleView import Grid, SampleView class LNLSSampleView(SampleView): @@ -17,21 +18,62 @@ def init(self): 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...") - beam_pos = HWR.beamline.beam.get_beam_position_on_screen() - self._bluesky_api.execute_plan( - plan_name="move_to_beam", - kwargs={ - "x_px": beam_pos[0] - x, - "y_px": y - beam_pos[1], - }, + 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()), ) - self.user_level_log.info("Move to beam has finished...") + 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( @@ -45,43 +87,19 @@ 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_procedure is not None: - self.user_level_log.exception("Already centring") - self.current_centring_procedure = "Manual" - self.emit("centringStarted", ("Manual")) - for step in range(nb_click): + 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() - beam_pos = HWR.beamline.beam.get_beam_position_on_screen() if (self.x is not None) and (self.y is not None): - self._bluesky_api.execute_plan( - plan_name="manual_alignment", - kwargs={ - "x_px": beam_pos[0] - self.x, - "y_px": self.y - beam_pos[1], - "step": step, - }, - ) + 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.centring_done() - self.accept_centring() - self.emit("centringSuccessful", ("Manual", self.get_centring_status())) - # Clear all shapes at frontend when 3-click centring finishes - self.shapes.clear() - self.frontend_application.server.emit( - "update_shapes", {"shapes": self.shapes}, namespace="/hwr" - ) - - def start_auto_centring(self): - self.user_level_log.info("Initializing automatic sample alignment...") - if self.current_centring_procedure is not None: - self.user_level_log.exception("Already centring") - self.current_centring_procedure = "Automatic" - self.emit("centringStarted", ("Automatic")) - self._bluesky_api.execute_plan(plan_name="automatic_alignment") - self.user_level_log.info("Automatic sample alignment has finished...") - self.centring_done() - self.accept_centring() + self.finish_centring() def get_snapshot(self): return None @@ -89,5 +107,66 @@ def get_snapshot(self): def _wait_for_centring_finishes(self): return - def get_centred_point_from_coord(self, x, y, return_by_names=None): # noqa: ARG002 - return {"omega": 0, "phiy": 0, "phiz": 0, "sampx": 0, "sampy": 0} + 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")