From 6c65d5546c352e1387444089840f0297f1f9a9ad Mon Sep 17 00:00:00 2001 From: gac-x01dc Date: Mon, 22 Sep 2025 13:40:22 +0200 Subject: [PATCH 1/6] fixed issue with flomni storage not updating and start using OMNYTools for yesno --- .../plugins/flomni/flomni.py | 162 ++++++++------- .../plugins/flomni/gui_tools.py | 193 ++++++++++++++++++ csaxs_bec/device_configs/flomni_config.yaml | 6 +- csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py | 3 + csaxs_bec/scans/flomni_fermat_scan.py | 4 +- 5 files changed, 296 insertions(+), 72 deletions(-) create mode 100644 csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index 94508a4..6c16e31 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -14,6 +14,8 @@ from typeguard import typechecked from csaxs_bec.bec_ipython_client.plugins.cSAXS import cSAXSBeamlineChecks from csaxs_bec.bec_ipython_client.plugins.flomni.flomni_optics_mixin import FlomniOpticsMixin from csaxs_bec.bec_ipython_client.plugins.flomni.x_ray_eye_align import XrayEyeAlign +from csaxs_bec.bec_ipython_client.plugins.flomni.gui_tools import flomniGuiTools +from csaxs_bec.bec_ipython_client.plugins.omny.omny_general_tools import OMNYTools logger = bec_logger.logger @@ -24,27 +26,57 @@ if builtins.__dict__.get("bec") is not None: umvr = builtins.__dict__.get("umvr") +class FlomniToolsError(Exception): + pass + class FlomniInitError(Exception): pass - class FlomniError(Exception): pass +class FlomniTools: + def yesno(self, message: str, default="none", autoconfirm=0) -> bool: + if autoconfirm and default == "y": + self.printgreen(message + " Automatically confirming default: yes") + return True + elif autoconfirm and default == "n": + self.printgreen(message + " Automatically confirming default: no") + return False + if default == "y": + message_ending = " [Y]/n? " + elif default == "n": + message_ending = " y/[N]? " + else: + message_ending = " y/n? " + while True: + user_input = input(self.OKBLUE + message + message_ending + self.ENDC) + if ( + user_input == "Y" or user_input == "y" or user_input == "yes" or user_input == "Yes" + ) or (default == "y" and user_input == ""): + return True + if ( + user_input == "N" or user_input == "n" or user_input == "no" or user_input == "No" + ) or (default == "n" and user_input == ""): + return False + else: + print("Please expicitely confirm y or n.") + + + + class FlomniInitStagesMixin: def flomni_init_stages(self): - user_input = input("Starting initialization of flOMNI stages. OK? [y/n]") - if user_input == "y": + if self.OMNYTools.yesno("Starting initialization of flOMNI stages. OK?"): print("staring...") else: return if self.check_all_axes_of_fomni_referenced(): - user_input = input("Continue anyways? [y/n]") - if user_input == "y": + if self.OMNYTools.yesno("All axes are referenced. Continue anyways?"): print("ok then...") else: return @@ -74,10 +106,8 @@ class FlomniInitStagesMixin: dev.feyex.limits = [-30, -1] print("done") - user_input = input( - "Init of foptz. Can the stage move to the upstream limit without collision? [y/n]" - ) - if user_input == "y": + + if self.OMNYTools.yesno("Init of foptz. Can the stage move to the upstream limit without collision?"): print("good then") else: return @@ -131,10 +161,7 @@ class FlomniInitStagesMixin: dev.fsamy.limits = [2, 3.1] print("done") - user_input = input( - "Init of tracking stages. Did you remove the outer laser flight tubes? [y/n]" - ) - if user_input == "y": + if self.OMNYTools.yesno("Init of tracking stages. Did you remove the outer laser flight tubes?"): print("good then") else: print("Stopping.") @@ -150,8 +177,7 @@ class FlomniInitStagesMixin: dev.ftrackz.limits = [4.5, 5.5] print("done") - user_input = input("Init of sample stage. Is the piezo at about 0 deg? [y/n]") - if user_input == "y": + if self.OMNYTools.yesno("Init of sample stage. Is the piezo at about 0 deg?"): print("good then") else: print("Stopping.") @@ -168,11 +194,7 @@ class FlomniInitStagesMixin: print("done") print("Initializing UPR stage.") - user_input = input( - "To ensure that the end switches work, please check that they are currently not pushed." - " Is everything okay? [y/n]" - ) - if user_input == "y": + if self.OMNYTools.yesno("To ensure that the end switches work, please check that they are currently not pushed. Is everything okay?"): print("good then") else: print("Stopping.") @@ -193,8 +215,7 @@ class FlomniInitStagesMixin: time.sleep(1) continue break - user_input = input("Shall I start the index search? [y/n]") - if user_input == "y": + if self.OMNYTools.yesno("Shall I start the index search?"): print("good then. Starting index search.") else: print("Stopping.") @@ -213,11 +234,7 @@ class FlomniInitStagesMixin: dev.fsamroy.limits = [-5, 365] print("done") - user_input = input( - "Init of foptx. Can the stage move to the positive limit without collision? Attention:" - " tracker flight tube! [y/n]" - ) - if user_input == "y": + if self.OMNYTools.yesno("Init of foptx. Can the stage move to the positive limit without collision? Attention: tracker flight tube!"): print("good then") else: print("Stopping.") @@ -241,8 +258,7 @@ class FlomniInitStagesMixin: continue break - user_input = input("Start limit switch search of fopty? [y/n]") - if user_input == "y": + if self.OMNYTools.yesno("Start limit switch search of fopty?"): print("good then") else: print("Stopping.") @@ -275,8 +291,7 @@ class FlomniInitStagesMixin: return False def set_limits(self): - user_input = input("Set default limits for flOMNI? [y/n]") - if user_input == "y": + if self.OMNYTools.yesno("Set default limits for flOMNI?"): print("setting limits...") else: print("Stopping.") @@ -303,8 +318,7 @@ class FlomniInitStagesMixin: dev.ftrackz.limits = [4.5, 5.5] def _align_setup(self): - user_input = input("Start moving stages to default initial positions? [y/n]") - if user_input == "y": + if self.OMNYTools.yesno("Start moving stages to default initial positions?", "y"): print("Start moving stages...") else: print("Stopping.") @@ -397,7 +411,8 @@ class FlomniSampleTransferMixin: raise FlomniError("Ftray is not at the 'IN' position. Aborting.") def ftransfer_flomni_stage_in(self): - sample_in_position = bool(float(dev.flomni_samples.sample_placed.sample0.get())) + sample_in_position = dev.flomni_samples.is_sample_slot_used(0) + #bool(float(dev.flomni_samples.sample_placed.sample0.get())) if not sample_in_position: raise FlomniError("There is no sample in the sample stage. Aborting.") self.reset_correction() @@ -496,22 +511,17 @@ class FlomniSampleTransferMixin: self.check_tray_in() self.check_sensor_connected() - sample_in_gripper = bool(float(dev.flomni_samples.sample_in_gripper.get())) + sample_in_gripper = dev.flomni_samples.is_sample_in_gripper() if sample_in_gripper: raise FlomniError( "The gripper does carry a sample. Cannot proceed getting another sample." ) - sample_signal = getattr(dev.flomni_samples.sample_placed, f"sample{position}") - sample_in_position = bool(float(sample_signal.get())) + sample_in_position = dev.flomni_samples.is_sample_slot_used(position) if not sample_in_position: raise FlomniError(f"The planned pick position [{position}] does not have a sample.") - user_input = input( - "Please confirm that there is currently no sample in the gripper. It would be dropped!" - " [y/n]" - ) - if user_input == "y": + if self.OMNYTools.yesno("Please confirm that there is currently no sample in the gripper. It would be dropped!", "y"): print("good then") else: print("Stopping.") @@ -555,12 +565,12 @@ class FlomniSampleTransferMixin: self.check_tray_in() self.check_sensor_connected() - sample_in_gripper = bool(float(dev.flomni_samples.sample_in_gripper.get())) + sample_in_gripper = dev.flomni_samples.is_sample_in_gripper() + #bool(float(dev.flomni_samples.sample_in_gripper.get())) if not sample_in_gripper: raise FlomniError("The gripper does not carry a sample.") - sample_signal = getattr(dev.flomni_samples.sample_placed, f"sample{position}") - sample_in_position = bool(float(sample_signal.get())) + sample_in_position = dev.flomni_samples.is_sample_slot_used(position) if sample_in_position: raise FlomniError(f"The planned put position [{position}] already has a sample.") @@ -605,29 +615,38 @@ class FlomniSampleTransferMixin: def ftransfer_sample_change(self, new_sample_position: int): self.check_tray_in() - sample_in_gripper = dev.flomni_samples.sample_in_gripper.get() +# sample_in_gripper = dev.flomni_samples.sample_in_gripper.get() + sample_in_gripper = dev.flomni_samples.is_sample_in_gripper() if sample_in_gripper: raise FlomniError("There is already a sample in the gripper. Aborting.") self.check_position_is_valid(new_sample_position) - sample_placed = getattr( - dev.flomni_samples.sample_placed, f"sample{new_sample_position}" - ).get() + if new_sample_position == 0: + raise FlomniError("The new sample to place cannot be the sample in the sample stage. Aborting.") + +# sample_placed = getattr( +# dev.flomni_samples.sample_placed, f"sample{new_sample_position}" +# ).get() + sample_placed = dev.flomni_samples.is_sample_slot_used(new_sample_position) if not sample_placed: raise FlomniError( f"There is currently no sample in position [{new_sample_position}]. Aborting." ) - sample_in_sample_stage = dev.flomni_samples.sample_placed.sample0.get() +# sample_in_sample_stage = dev.flomni_samples.sample_placed.sample0.get() + sample_in_sample_stage = dev.flomni_samples.is_sample_slot_used(0) if sample_in_sample_stage: # find a new home for the sample... empty_slots = [] - for name, val in dev.flomni_samples.read().items(): - if "flomni_samples_sample_placed_sample" not in name: - continue - if val.get("value") == 0: - empty_slots.append(int(name.split("flomni_samples_sample_placed_sample")[1])) +# for name, val in dev.flomni_samples.read().items(): +# if "flomni_samples_sample_placed_sample" not in name: +# continue +# if val.get("value") == 0: +# empty_slots.append(int(name.split("flomni_samples_sample_placed_sample")[1])) + for j in range(1,20): + if not dev.flomni_samples.is_sample_slot_used(j): + empty_slots.append(j) if not empty_slots: raise FlomniError("There are no empty slots available. Aborting.") @@ -700,8 +719,7 @@ class FlomniSampleTransferMixin: if confirm != -1: return - user_input = input("All OK? Continue? [y/n]") - if user_input == "y": + if self.OMNYTools.yesno("All OK? Continue?", "y"): print("good then") dev.ftransy.controller.socket_put_confirmed("confirm=1") else: @@ -713,7 +731,8 @@ class FlomniSampleTransferMixin: return status def ftransfer_gripper_open(self): - sample_in_gripper = dev.flomni_samples.sample_in_gripper.get() + sample_in_gripper = dev.flomni_samples.is_sample_in_gripper() + #dev.flomni_samples.sample_in_gripper.get() if sample_in_gripper: raise FlomniError( "Cannot open gripper. There is still a sample in the gripper! Aborting." @@ -733,11 +752,8 @@ class FlomniSampleTransferMixin: fsamx_pos = dev.fsamx.readback.get() if position == 0 and fsamx_pos > -160: - user_input = input( - "May the flomni stage be moved out for the sample change? Feedback will be disabled" - " and alignment will be lost! [y/n]" - ) - if user_input == "y": + + if self.OMNYTools.yesno("May the flomni stage be moved out for the sample change? Feedback will be disabled and alignment will be lost!", "y"): print("good then") self.ftransfer_flomni_stage_out() else: @@ -1106,6 +1122,7 @@ class Flomni( FlomniAlignmentMixin, FlomniOpticsMixin, cSAXSBeamlineChecks, + flomniGuiTools ): def __init__(self, client): super().__init__() @@ -1128,13 +1145,20 @@ class Flomni( self.corr_pos_y_2 = [] self.corr_angle_y_2 = [] self.progress = {} + self.progress["subtomo"] = 0 + self.progress["subtomo_projection"] = 0 + self.progress["subtomo_total_projections"] = 1 + self.progress["projection"] = 0 + self.progress["total_projections"] = 1 + self.progress["angle"] = 0 + self.progress["tomo_type"] = 0 + self.OMNYTools = OMNYTools(self.client) self.align = XrayEyeAlign(self.client, self) + self.set_client(client) def start_x_ray_eye_alignment(self): - user_input = input( - "Starting Xrayeye alignment. Deleting any potential existing alignment for this sample. [Y/n]" - ) - if user_input == "y" or user_input == "": + + if self.OMNYTools.yesno("Starting Xrayeye alignment. Deleting any potential existing alignment for this sample.", "y"): self.align = XrayEyeAlign(self.client, self) try: self.align.align() @@ -1894,8 +1918,8 @@ class Flomni( ) print(f"\nSample name: {self.sample_name}\n") - user_input = input("Are these parameters correctly set for your scan? [Y/n]") - if user_input == "y" or user_input == "": + + if self.OMNYTools.yesno("Are these parameters correctly set for your scan?", "y"): print("... excellent!") else: self.tomo_countingtime = self._get_val(" s", self.tomo_countingtime, float) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py b/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py new file mode 100644 index 0000000..2934072 --- /dev/null +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py @@ -0,0 +1,193 @@ +import builtins + +from bec_widgets.cli.client import BECDockArea + +# from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose + +if builtins.__dict__.get("bec") is not None: + bec = builtins.__dict__.get("bec") + dev = builtins.__dict__.get("dev") + umv = builtins.__dict__.get("umv") + umvr = builtins.__dict__.get("umvr") + + +class flomniGuiToolsError(Exception): + pass + + +class flomniGuiTools: + + def __init__(self): + self.gui_window = None + self.fig200 = None + self.fig201 = None + self.fig202 = None + self.fig203 = None + self.progressbar = None + self.text_box = None + self.idle_text_box = None + + def set_client(self, client): + self.client = client + self.gui = self.client.gui + + def flomnigui_show_gui(self): + if "flomni" in self.gui.windows: + self.gui.flomni.show() + else: + self.gui.new("flomni") + self.gui_window = self.gui.flomni + + def flomnigui_stop_gui(self): + self.gui_window.hide() + + def _flomnicam_parking(self): + self.flomnigui_show_flomnicam_parking() + + def flomnigui_show_flomnicam_parking(self): + self.flomnigui_show_gui() + if self.fig200 is None: + self._flomnicam_clear() + self.fig200 = self.gui_window.add_dock(name="flomnicam200").add_widget("BECImageWidget") + if self._flomnicam_check_device_exists(dev.cam200): + fig = self.fig200.image("cam200") + fig.set_rotation(deg_90=3) + self.fig200.lock_aspect_ratio(True) + else: + print("Cannot open cam200. Device does not exist.") + self.fig203 = self.gui_window.add_dock(name="flomnicam203").add_widget("BECImageWidget") + if self._flomnicam_check_device_exists(dev.cam203): + fig = self.fig203.image("cam203") + fig.set_rotation(deg_90=3) + self.fig203.lock_aspect_ratio(True) + else: + print("Cannot open cam203. Device does not exist.") + try: + self.gui_window.remove_dock(name="default_figure") + except: + pass + + def flomnigui_remove_all_docks(self): + self.gui_window.delete_all() + self.fig200 = None + self.fig201 = None + self.fig202 = None + self.fig203 = None + self.progressbar = None + self.text_box = None + self.idle_text_box = None + + def flomnigui_idle(self): + self.flomnigui_show_gui() + if self.idle_text_box is None: + self.flomnigui_remove_all_docks() + self.idle_text_box = self.gui_window.add_dock(name="idle_text").add_widget("TextBox") + try: + self.gui_window.remove_dock(name="default_figure") + except: + pass + text = ( + "
"
+            + "    ,o888888o.           ,8.       ,8.          b.             8 `8.`8888.      ,8' \n"
+            + " . 8888     `88.        ,888.     ,888.         888o.          8  `8.`8888.    ,8'  \n"
+            + ",8 8888       `8b      .`8888.   .`8888.        Y88888o.       8   `8.`8888.  ,8'   \n"
+            + "88 8888        `8b    ,8.`8888. ,8.`8888.       .`Y888888o.    8    `8.`8888.,8'    \n"
+            + "88 8888         88   ,8'8.`8888,8^8.`8888.      8o. `Y888888o. 8     `8.`88888'     \n"
+            + "88 8888         88  ,8' `8.`8888' `8.`8888.     8`Y8o. `Y88888o8      `8. 8888      \n"
+            + "88 8888        ,8P ,8'   `8.`88'   `8.`8888.    8   `Y8o. `Y8888       `8 8888      \n"
+            + "`8 8888       ,8P ,8'     `8.`'     `8.`8888.   8      `Y8o. `Y8        8 8888      \n"
+            + " ` 8888     ,88' ,8'       `8        `8.`8888.  8         `Y8o.`        8 8888      \n"
+            + "    `8888888P'  ,8'         `         `8.`8888. 8            `Yo        8 8888      \n"
+            + "
" + ) + self.idle_text_box.set_html_text(text) + + def _flomnicam_clear(self): + self.flomnigui_remove_all_docks() + + def _flomnicam_check_device_exists(self, device): + try: + device + except: + return False + else: + return True + + def _flomnicam_samplestage(self): + self.flomnigui_show_flomnicam_samplestage() + + def flomnigui_show_flomnicam_samplestage(self): + self.flomnigui_show_gui() + if self.fig201 is None: + self.flomnigui_remove_all_docks() + self.fig201 = self.gui_window.add_dock(name="flomnicam201").add_widget("BECImageWidget") + if self._flomnicam_check_device_exists(dev.cam201): + fig = self.fig201.image("cam201") + fig.set_rotation(deg_90=3) + self.fig201.lock_aspect_ratio(True) + else: + print("Cannot open cam201. Device does not exist.") + self.fig202 = self.gui_window.add_dock(name="flomnicam202").add_widget("BECImageWidget") + if self._flomnicam_check_device_exists(dev.cam202): + fig = self.fig202.image("cam202") + fig.set_rotation(deg_90=3) + self.fig202.lock_aspect_ratio(True) + else: + print("Cannot open cam202. Device does not exist.") + try: + self.gui_window.remove_dock(name="default_figure") + except: + pass + + def flomnigui_show_progress(self): + self.flomnigui_show_gui() + if self.progressbar is None: + self.flomnigui_remove_all_docks() + # Add a new dock with a RingProgressBar widget + self.progressbar = self.gui_window.new(name="progress").new("RingProgressBar") + # Customize the size of the progress ring + self.progressbar.set_line_widths(20) + # Disable automatic updates and manually set the self.progressbar value + self.progressbar.enable_auto_updates(False) + # Set precision for the self.progressbar display + self.progressbar.set_precision(1) # Display self.progressbar with one decimal places + # Setting multiple rigns with different values + self.progressbar.set_number_of_bars(3) + self.progressbar.rings[2].set_update("scan") + # Set the values of the rings to 50, 75, and 25 from outer to inner ring + # self.progressbar.set_value([50, 75]) + # Add a new dock with a TextBox widget + self.text_box = self.gui_window.new(name="progress_text").new("TextBox") + try: + self.gui_window.bec.delete() + except: + pass + self._flomnigui_update_progress() + + def _flomnigui_update_progress(self): + if self.progressbar is not None: + progress = self.progress["projection"] / self.progress["total_projections"] * 100 + subotmo_progress = ( + self.progress["subtomo_projection"] + / self.progress["subtomo_total_projections"] + * 100 + ) + self.progressbar.set_value([progress, subotmo_progress]) + + text = f"Progress report:\n Tomo type: ....................... {self.progress['tomo_type']}\n Projection: ...................... {self.progress['projection']:.0f}\n Total projections expected ....... {self.progress['total_projections']}\n Angle: ........................... {self.progress['angle']}\n Current subtomo: ................. {self.progress['subtomo']}\n Current projection within subtomo: {self.progress['subtomo_projection']}\n Total projections per subtomo: ... {self.progress['subtomo_total_projections']}" + self.text_box.set_plain_text(text) + + + +if __name__ == "__main__": + from bec_lib.client import BECClient + from bec_widgets.cli.client_utils import BECGuiClient + client = BECClient() + client.start() + client.gui = BECGuiClient() + + + + flomni_gui = flomniGuiTools(client) + flomni_gui.flomnigui_show_gui() + flomni_gui.flomnigui_show_progress() \ No newline at end of file diff --git a/csaxs_bec/device_configs/flomni_config.yaml b/csaxs_bec/device_configs/flomni_config.yaml index 1048797..3221331 100644 --- a/csaxs_bec/device_configs/flomni_config.yaml +++ b/csaxs_bec/device_configs/flomni_config.yaml @@ -213,6 +213,8 @@ ftransy: onFailure: buffer readOnly: false readoutPriority: baseline + userParameter: + sensor_voltage: -2.4 ftransz: description: Sample transer Z deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor @@ -333,8 +335,8 @@ rtx: readOnly: false readoutPriority: on_request userParameter: - low_signal: 11000 - min_signal: 10000 + low_signal: 10000 + min_signal: 9000 rt_pid_voltage: -0.06219 rty: description: flomni rt diff --git a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py index 2ed072e..116ae10 100644 --- a/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py +++ b/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py @@ -392,6 +392,9 @@ class RtFlomniController(Controller): rtx = self.get_device_manager().devices.rtx min_signal = rtx.user_parameter.get("min_signal") low_signal = rtx.user_parameter.get("low_signal") + print(f"low signal: {low_signal}") + print(f"min signal: {min_signal}") + print(f"signal: {signal}") if signal < min_signal: time.sleep(1) if signal < min_signal: diff --git a/csaxs_bec/scans/flomni_fermat_scan.py b/csaxs_bec/scans/flomni_fermat_scan.py index 651f8f0..28528fa 100644 --- a/csaxs_bec/scans/flomni_fermat_scan.py +++ b/csaxs_bec/scans/flomni_fermat_scan.py @@ -33,11 +33,11 @@ logger = bec_logger.logger class FlomniFermatScan(SyncFlyScanBase): scan_name = "flomni_fermat_scan" - scan_report_hint = "table" scan_type = "fly" required_kwargs = ["fovx", "fovy", "exp_time", "step", "angle"] arg_input = {} arg_bundle_size = {"bundle": len(arg_input), "min": None, "max": None} + use_scan_progress_report = True def __init__( self, @@ -74,6 +74,7 @@ class FlomniFermatScan(SyncFlyScanBase): """ super().__init__(parameter=parameter, **kwargs) + self.show_live_table = False self.axis = [] self.fovx = fovx self.fovy = fovy @@ -168,6 +169,7 @@ class FlomniFermatScan(SyncFlyScanBase): tracker_signal_status = yield from self.stubs.send_rpc_and_wait( "rtx", "controller.laser_tracker_check_signalstrength" ) + self.device_manager.connector.send_client_info(tracker_signal_status) if tracker_signal_status == "low": self.device_manager.connector.raise_alarm( severity=0, -- 2.49.1 From 8f7914b978d658be36458d64b3db9aabf43c5283 Mon Sep 17 00:00:00 2001 From: gac-x01dc Date: Tue, 23 Sep 2025 15:29:04 +0200 Subject: [PATCH 2/6] - added camera viewer device - fixed some issues in flomni sample storage device --- csaxs_bec/device_configs/flomni_config.yaml | 26 ++++++++ .../devices/omny/flomni_sample_storage.py | 7 +- csaxs_bec/devices/omny/omny_sample_storage.py | 28 ++++---- csaxs_bec/devices/omny/webcam_viewer.py | 65 +++++++++++++++++++ 4 files changed, 110 insertions(+), 16 deletions(-) create mode 100644 csaxs_bec/devices/omny/webcam_viewer.py diff --git a/csaxs_bec/device_configs/flomni_config.yaml b/csaxs_bec/device_configs/flomni_config.yaml index 3221331..24e9679 100644 --- a/csaxs_bec/device_configs/flomni_config.yaml +++ b/csaxs_bec/device_configs/flomni_config.yaml @@ -364,3 +364,29 @@ rtz: onFailure: buffer readOnly: false readoutPriority: on_request + + + +cam_flomni_gripper: + description: Camera sample changer + deviceClass: csaxs_bec.devices.omny.webcam_viewer.WebcamViewer + deviceConfig: + url: http://flomnicamserver:5000/video_high + num_rotation_90: 3 + transpose: false + enabled: true + onFailure: buffer + readOnly: false + readoutPriority: on_request + +cam_flomni_overview: + description: Camera flomni overview + deviceClass: csaxs_bec.devices.omny.webcam_viewer.WebcamViewer + deviceConfig: + url: http://flomnicamserver:5001/video_high + num_rotation_90: 3 + transpose: false + enabled: true + onFailure: buffer + readOnly: false + readoutPriority: on_request \ No newline at end of file diff --git a/csaxs_bec/devices/omny/flomni_sample_storage.py b/csaxs_bec/devices/omny/flomni_sample_storage.py index bf154fa..ca8c940 100644 --- a/csaxs_bec/devices/omny/flomni_sample_storage.py +++ b/csaxs_bec/devices/omny/flomni_sample_storage.py @@ -24,24 +24,25 @@ class FlomniSampleStorage(Device): SUB_VALUE = "value" _default_sub = SUB_VALUE sample_placed = { - f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_flomni{i}:GET", {}) for i in range(21) + f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_flomni{i}:GET", {"auto_monitor": True}) for i in range(21) } sample_placed = Dcpt(sample_placed) sample_names = { - f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_flomni{i}:GET.DESC", {"string": True}) + f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_flomni{i}:GET.DESC", {"string": True, "auto_monitor": True}) for i in range(21) } sample_names = Dcpt(sample_names) sample_in_gripper = Cpt( - EpicsSignal, name="sample_in_gripper", read_pv="XOMNY-SAMPLE_DB_flomni100:GET" + EpicsSignal, name="sample_in_gripper", read_pv="XOMNY-SAMPLE_DB_flomni100:GET", auto_monitor=True ) sample_in_gripper_name = Cpt( EpicsSignal, name="sample_in_gripper_name", read_pv="XOMNY-SAMPLE_DB_flomni100:GET.DESC", string=True, + auto_monitor=True ) def __init__(self, prefix="", *, name, **kwargs): diff --git a/csaxs_bec/devices/omny/omny_sample_storage.py b/csaxs_bec/devices/omny/omny_sample_storage.py index 22da15c..4675176 100644 --- a/csaxs_bec/devices/omny/omny_sample_storage.py +++ b/csaxs_bec/devices/omny/omny_sample_storage.py @@ -37,84 +37,86 @@ class OMNYSampleStorage(Device): _default_sub = SUB_VALUE sample_shuttle_A_placed = { - f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_A:{i}", {}) for i in range(1, 7) + f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_A:{i}", {"auto_monitor": True}) for i in range(1, 7) } sample_shuttle_A_placed = Dcpt(sample_shuttle_A_placed) sample_shuttle_B_placed = { - f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_B:{i}", {}) for i in range(1, 7) + f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_B:{i}", {"auto_monitor": True}) for i in range(1, 7) } sample_shuttle_B_placed = Dcpt(sample_shuttle_B_placed) sample_shuttle_C_placed = { - f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}", {}) for i in range(1, 7) + f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}", {"auto_monitor": True}) for i in range(1, 7) } sample_shuttle_C_placed = Dcpt(sample_shuttle_C_placed) sample_shuttle_C_placed = { - f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}", {}) for i in range(1, 7) + f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}", {"auto_monitor": True}) for i in range(1, 7) } sample_shuttle_C_placed = Dcpt(sample_shuttle_C_placed) parking_placed = { - f"parking{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_parking:{i}", {}) for i in range(1, 7) + f"parking{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_parking:{i}", {"auto_monitor": True}) for i in range(1, 7) } parking_placed = Dcpt(parking_placed) sample_placed = { - f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_omny:{i}", {}) + f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_omny:{i}", {"auto_monitor": True}) for i in [10, 11, 12, 13, 14, 32, 33, 34, 100, 101] } sample_placed = Dcpt(sample_placed) sample_shuttle_A_names = { - f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_A:{i}.DESC", {"string": True}) + f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_A:{i}.DESC", {"string": True, "auto_monitor": True}) for i in range(1, 7) } sample_shuttle_A_names = Dcpt(sample_shuttle_A_names) sample_shuttle_B_names = { - f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_B:{i}.DESC", {"string": True}) + f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_B:{i}.DESC", {"string": True, "auto_monitor": True}) for i in range(1, 7) } sample_shuttle_B_names = Dcpt(sample_shuttle_B_names) sample_shuttle_C_names = { - f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}.DESC", {"string": True}) + f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_shuttle_C:{i}.DESC", {"string": True, "auto_monitor": True}) for i in range(1, 7) } sample_shuttle_C_names = Dcpt(sample_shuttle_C_names) parking_names = { - f"parking{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_parking:{i}.DESC", {"string": True}) + f"parking{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_parking:{i}.DESC", {"string": True, "auto_monitor": True}) for i in range(1, 7) } parking_names = Dcpt(parking_names) sample_names = { - f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_omny:{i}.DESC", {"string": True}) + f"sample{i}": (EpicsSignal, f"XOMNY-SAMPLE_DB_omny:{i}.DESC", {"string": True, "auto_monitor": True}) for i in [10, 11, 12, 13, 14, 32, 33, 34, 100, 101] } sample_names = Dcpt(sample_names) sample_in_gripper = Cpt( - EpicsSignal, name="sample_in_gripper", read_pv="XOMNY-SAMPLE_DB_omny:110.VAL" + EpicsSignal, name="sample_in_gripper", read_pv="XOMNY-SAMPLE_DB_omny:110.VAL", auto_monitor=True ) sample_in_gripper_name = Cpt( EpicsSignal, name="sample_in_gripper_name", read_pv="XOMNY-SAMPLE_DB_omny:110.DESC", string=True, + auto_monitor=True ) sample_in_samplestage = Cpt( - EpicsSignal, name="sample_in_samplestage", read_pv="XOMNY-SAMPLE_DB_omny:0.VAL" + EpicsSignal, name="sample_in_samplestage", read_pv="XOMNY-SAMPLE_DB_omny:0.VAL", auto_monitor=True ) sample_in_samplestage_name = Cpt( EpicsSignal, name="sample_in_samplestage_name", read_pv="XOMNY-SAMPLE_DB_omny:0.DESC", string=True, + auto_monitor=True ) def __init__(self, prefix="", *, name, **kwargs): diff --git a/csaxs_bec/devices/omny/webcam_viewer.py b/csaxs_bec/devices/omny/webcam_viewer.py new file mode 100644 index 0000000..3bfecb2 --- /dev/null +++ b/csaxs_bec/devices/omny/webcam_viewer.py @@ -0,0 +1,65 @@ +import requests +import threading +import cv2 +import numpy as np +from ophyd import Device, Component as Cpt +from ophyd_devices import PreviewSignal +import traceback + +from bec_lib.logger import bec_logger + +logger = bec_logger.logger + +class WebcamViewer(Device): + USER_ACCESS = ["start", "stop"] + preview = Cpt(PreviewSignal, ndim=2, num_rotation_90=0, transpose=False) + + def __init__(self, url:str, name:str, num_rotation_90=0, transpose=False, **kwargs) -> None: + super().__init__(name=name, **kwargs) + self.url = url + self._connection = None + self._update_thread = None + self._buffer = b"" + self._shutdown_event = threading.Event() + self.preview.num_rotation_90 = num_rotation_90 + self.preview.transpose = transpose + + def start(self) -> None: + if self._connection is not None: + return + self._update_thread = threading.Thread(target=self._update_loop, daemon=True) + self._update_thread.start() + + def _update_loop(self) -> None: + while not self._shutdown_event.is_set(): + try: + self._connection = requests.get(self.url, stream=True) + for chunk in self._connection.iter_content(chunk_size=1024): + self._buffer += chunk + start = self._buffer.find(b'\xff\xd8') # JPEG start + end = self._buffer.find(b'\xff\xd9') # JPEG end + if start == -1 or end == -1: + continue + jpg = self._buffer[start:end+2] + self._buffer = self._buffer[end+2:] + + image = cv2.imdecode(np.frombuffer(jpg, np.uint8), cv2.IMREAD_COLOR) + if image is not None: + image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) + self.preview.put(image) + except Exception as exc: + content = traceback.format_exc() + logger.error(f"Image update loop failed: {content}") + + def stop(self) -> None: + if self._connection is None: + return + self._shutdown_event.set() + if self._connection is not None: + self._connection.close() + self._connection = None + if self._update_thread is not None: + self._update_thread.join() + self._update_thread = None + + self._shutdown_event.clear() \ No newline at end of file -- 2.49.1 From f526d5cc053c2dd21f8e52ae7e3382fbfc5cb482 Mon Sep 17 00:00:00 2001 From: x01dc Date: Thu, 25 Sep 2025 16:42:40 +0200 Subject: [PATCH 3/6] gui tools running --- .../plugins/flomni/gui_tools.py | 127 ++++++------------ pyproject.toml | 1 + 2 files changed, 43 insertions(+), 85 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py b/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py index 2934072..1089b19 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py @@ -19,10 +19,8 @@ class flomniGuiTools: def __init__(self): self.gui_window = None - self.fig200 = None - self.fig201 = None - self.fig202 = None - self.fig203 = None + self.camera_gripper_image = None + self.camera_overview_image = None self.progressbar = None self.text_box = None self.idle_text_box = None @@ -36,43 +34,43 @@ class flomniGuiTools: self.gui.flomni.show() else: self.gui.new("flomni") - self.gui_window = self.gui.flomni def flomnigui_stop_gui(self): - self.gui_window.hide() + self.gui.flomni.hide() - def _flomnicam_parking(self): - self.flomnigui_show_flomnicam_parking() - - def flomnigui_show_flomnicam_parking(self): + def flomnigui_show_cameras(self): self.flomnigui_show_gui() - if self.fig200 is None: - self._flomnicam_clear() - self.fig200 = self.gui_window.add_dock(name="flomnicam200").add_widget("BECImageWidget") - if self._flomnicam_check_device_exists(dev.cam200): - fig = self.fig200.image("cam200") - fig.set_rotation(deg_90=3) - self.fig200.lock_aspect_ratio(True) + if self.camera_gripper_image is None or self.camera_overview_image is None: + self.flomnigui_remove_all_docks() + self.camera_gripper_image = self.gui.flomni.new("camera_gripper").new("Image") + if self._flomnicam_check_device_exists(dev.cam_flomni_gripper): + self.camera_gripper_image.image(("cam_flomni_gripper","preview")) + self.camera_gripper_image.lock_aspect_ratio = True + self.camera_gripper_image.enable_fps_monitor = True + self.camera_gripper_image.enable_toolbar = False + self.camera_gripper_image.outer_axes = False + self.camera_gripper_image.inner_axes = False + dev.cam_flomni_gripper.start() else: - print("Cannot open cam200. Device does not exist.") - self.fig203 = self.gui_window.add_dock(name="flomnicam203").add_widget("BECImageWidget") - if self._flomnicam_check_device_exists(dev.cam203): - fig = self.fig203.image("cam203") - fig.set_rotation(deg_90=3) - self.fig203.lock_aspect_ratio(True) + print("Cannot open camera_gripper. Device does not exist.") + self.camera_overview_image = self.gui.flomni.new("camera_overview").new("Image") + if self._flomnicam_check_device_exists(dev.cam_flomni_overview): + self.camera_overview_image.image(("cam_flomni_overview","preview")) + self.camera_overview_image.lock_aspect_ratio = True + self.camera_overview_image.enable_fps_monitor = True + self.camera_overview_image.enable_toolbar = False + self.camera_overview_image.outer_axes = False + self.camera_overview_image.inner_axes = False + dev.cam_flomni_overview.start() else: - print("Cannot open cam203. Device does not exist.") - try: - self.gui_window.remove_dock(name="default_figure") - except: - pass + print("Cannot open camera_overview. Device does not exist.") def flomnigui_remove_all_docks(self): - self.gui_window.delete_all() - self.fig200 = None - self.fig201 = None - self.fig202 = None - self.fig203 = None + dev.cam_flomni_overview.stop() + dev.cam_flomni_gripper.stop() + self.gui.flomni.delete_all() + self.camera_gripper_image = None + self.camera_overview_image = None self.progressbar = None self.text_box = None self.idle_text_box = None @@ -81,30 +79,18 @@ class flomniGuiTools: self.flomnigui_show_gui() if self.idle_text_box is None: self.flomnigui_remove_all_docks() - self.idle_text_box = self.gui_window.add_dock(name="idle_text").add_widget("TextBox") - try: - self.gui_window.remove_dock(name="default_figure") - except: - pass + self.idle_text_box = self.gui.flomni.new("idle_textbox").new("TextBox") text = ( - "
"
-            + "    ,o888888o.           ,8.       ,8.          b.             8 `8.`8888.      ,8' \n"
-            + " . 8888     `88.        ,888.     ,888.         888o.          8  `8.`8888.    ,8'  \n"
-            + ",8 8888       `8b      .`8888.   .`8888.        Y88888o.       8   `8.`8888.  ,8'   \n"
-            + "88 8888        `8b    ,8.`8888. ,8.`8888.       .`Y888888o.    8    `8.`8888.,8'    \n"
-            + "88 8888         88   ,8'8.`8888,8^8.`8888.      8o. `Y888888o. 8     `8.`88888'     \n"
-            + "88 8888         88  ,8' `8.`8888' `8.`8888.     8`Y8o. `Y88888o8      `8. 8888      \n"
-            + "88 8888        ,8P ,8'   `8.`88'   `8.`8888.    8   `Y8o. `Y8888       `8 8888      \n"
-            + "`8 8888       ,8P ,8'     `8.`'     `8.`8888.   8      `Y8o. `Y8        8 8888      \n"
-            + " ` 8888     ,88' ,8'       `8        `8.`8888.  8         `Y8o.`        8 8888      \n"
-            + "    `8888888P'  ,8'         `         `8.`8888. 8            `Yo        8 8888      \n"
-            + "
" + "
"                                       
+            + " ,---.,--. ,-----. ,--.   ,--.,--.  ,--.,--. \n"
+            + "/  .-'|  |'  .-.  '|   `.'   ||  ,'.|  ||  | \n"
+            + "|  `-,|  ||  | |  ||  |'.'|  ||  |' '  ||  | \n"
+            + "|  .-'|  |'  '-'  '|  |   |  ||  | `   ||  | \n"
+            + "`--'  `--' `-----' `--'   `--'`--'  `--'`--' \n"
+            + "
" ) self.idle_text_box.set_html_text(text) - def _flomnicam_clear(self): - self.flomnigui_remove_all_docks() - def _flomnicam_check_device_exists(self, device): try: device @@ -113,38 +99,12 @@ class flomniGuiTools: else: return True - def _flomnicam_samplestage(self): - self.flomnigui_show_flomnicam_samplestage() - - def flomnigui_show_flomnicam_samplestage(self): - self.flomnigui_show_gui() - if self.fig201 is None: - self.flomnigui_remove_all_docks() - self.fig201 = self.gui_window.add_dock(name="flomnicam201").add_widget("BECImageWidget") - if self._flomnicam_check_device_exists(dev.cam201): - fig = self.fig201.image("cam201") - fig.set_rotation(deg_90=3) - self.fig201.lock_aspect_ratio(True) - else: - print("Cannot open cam201. Device does not exist.") - self.fig202 = self.gui_window.add_dock(name="flomnicam202").add_widget("BECImageWidget") - if self._flomnicam_check_device_exists(dev.cam202): - fig = self.fig202.image("cam202") - fig.set_rotation(deg_90=3) - self.fig202.lock_aspect_ratio(True) - else: - print("Cannot open cam202. Device does not exist.") - try: - self.gui_window.remove_dock(name="default_figure") - except: - pass - def flomnigui_show_progress(self): self.flomnigui_show_gui() if self.progressbar is None: self.flomnigui_remove_all_docks() # Add a new dock with a RingProgressBar widget - self.progressbar = self.gui_window.new(name="progress").new("RingProgressBar") + self.progressbar = self.gui.flomni.new("progressbar").new("RingProgressBar") # Customize the size of the progress ring self.progressbar.set_line_widths(20) # Disable automatic updates and manually set the self.progressbar value @@ -157,11 +117,8 @@ class flomniGuiTools: # Set the values of the rings to 50, 75, and 25 from outer to inner ring # self.progressbar.set_value([50, 75]) # Add a new dock with a TextBox widget - self.text_box = self.gui_window.new(name="progress_text").new("TextBox") - try: - self.gui_window.bec.delete() - except: - pass + self.text_box = self.gui.flomni.new(name="progress_text").new("TextBox") + self._flomnigui_update_progress() def _flomnigui_update_progress(self): diff --git a/pyproject.toml b/pyproject.toml index 269d369..5cadf8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ dependencies = [ "pyueye", # for the IDS uEye camera "bec_widgets", "zmq", + "opencv-python", ] [project.optional-dependencies] -- 2.49.1 From d16f6b703cb0dabcf5797f91cb137caa3941a633 Mon Sep 17 00:00:00 2001 From: x01dc Date: Mon, 6 Oct 2025 15:54:13 +0200 Subject: [PATCH 4/6] tested gui tools --- .../plugins/flomni/flomni.py | 30 +++++++++++++++---- .../plugins/flomni/gui_tools.py | 14 +++++---- csaxs_bec/devices/omny/webcam_viewer.py | 6 ++-- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index 6c16e31..e2a0f33 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -425,6 +425,8 @@ class FlomniSampleTransferMixin: umv(dev.fsamx, fsamx_in) dev.fsamx.limits = [fsamx_in - 0.4, fsamx_in + 0.4] + self.flomnigui_idle() + def laser_tracker_show_all(self): dev.rtx.controller.laser_tracker_show_all() @@ -464,6 +466,10 @@ class FlomniSampleTransferMixin: self.device_manager.devices.fsamx.controller.lights_on() def ftransfer_flomni_stage_out(self): + + self.flomnigui_show_cameras() + + target_pos = -162 if np.isclose(dev.fsamx.readback.get(), target_pos, 0.01): return @@ -521,6 +527,9 @@ class FlomniSampleTransferMixin: if not sample_in_position: raise FlomniError(f"The planned pick position [{position}] does not have a sample.") + self.flomnigui_show_cameras() + + if self.OMNYTools.yesno("Please confirm that there is currently no sample in the gripper. It would be dropped!", "y"): print("good then") else: @@ -603,8 +612,9 @@ class FlomniSampleTransferMixin: self.flomni_modify_storage_non_interactive(100, 0, "-") self.flomni_modify_storage_non_interactive(position, 1, sample_name) - # TODO: flomni_stage_in if position == 0 - # bec.queue.next_dataset_number += 1 + if position == 0: + self.ftransfer_flomni_stage_in() + bec.queue.next_dataset_number += 1 def sample_get_name(self, position: int = 0) -> str: """ @@ -653,7 +663,13 @@ class FlomniSampleTransferMixin: print(f"The following slots are empty: {empty_slots}.") while True: - user_input = input(f"Where shall I put the sample? Default: [{empty_slots[0]}]") + user_input = input(f"Where shall I put the sample? Default: [{empty_slots[0]}] ") + + if user_input.strip() == "": + # No entry: use default + user_input = empty_slots[0] + break + try: user_input = int(user_input) if user_input not in empty_slots: @@ -724,7 +740,7 @@ class FlomniSampleTransferMixin: dev.ftransy.controller.socket_put_confirmed("confirm=1") else: print("Stopping.") - return + exit def ftransfer_gripper_is_open(self) -> bool: status = bool(float(dev.ftransy.controller.socket_put_and_receive("MG @OUT[9]").strip())) @@ -1594,6 +1610,9 @@ class Flomni( def tomo_scan(self, subtomo_start=1, start_angle=None, projection_number=None): """start a tomo scan""" + + self.flomnigui_show_progress() + bec = builtins.__dict__.get("bec") scans = builtins.__dict__.get("scans") self._current_special_angles = self.special_angles.copy() @@ -1730,6 +1749,7 @@ class Flomni( print(f"Angle: ........................... {self.progress['angle']}") print(f"Current subtomo: ................. {self.progress['subtomo']}") print(f"Current projection within subtomo: {self.progress['subtomo_projection']}\x1b[0m") + self._flomnigui_update_progress() def add_sample_database( self, samplename, date, eaccount, scan_number, setup, sample_additional_info, user @@ -1819,7 +1839,7 @@ class Flomni( def _write_tomo_scan_number(self, scan_number: int, angle: float, subtomo_number: int) -> None: tomo_scan_numbers_file = os.path.expanduser( - "~/Data10/specES1/dat-files/tomography_scannumbers.txt" + "~/tomography_scannumbers.txt" ) with open(tomo_scan_numbers_file, "a+") as out_file: # pylint: disable=undefined-variable diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py b/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py index 1089b19..aef6ca3 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py @@ -50,7 +50,7 @@ class flomniGuiTools: self.camera_gripper_image.enable_toolbar = False self.camera_gripper_image.outer_axes = False self.camera_gripper_image.inner_axes = False - dev.cam_flomni_gripper.start() + dev.cam_flomni_gripper.start_live_mode() else: print("Cannot open camera_gripper. Device does not exist.") self.camera_overview_image = self.gui.flomni.new("camera_overview").new("Image") @@ -61,13 +61,13 @@ class flomniGuiTools: self.camera_overview_image.enable_toolbar = False self.camera_overview_image.outer_axes = False self.camera_overview_image.inner_axes = False - dev.cam_flomni_overview.start() + dev.cam_flomni_overview.start_live_mode() else: print("Cannot open camera_overview. Device does not exist.") def flomnigui_remove_all_docks(self): - dev.cam_flomni_overview.stop() - dev.cam_flomni_gripper.stop() + dev.cam_flomni_overview.stop_live_mode() + dev.cam_flomni_gripper.stop_live_mode() self.gui.flomni.delete_all() self.camera_gripper_image = None self.camera_overview_image = None @@ -113,6 +113,8 @@ class flomniGuiTools: self.progressbar.set_precision(1) # Display self.progressbar with one decimal places # Setting multiple rigns with different values self.progressbar.set_number_of_bars(3) + self.progressbar.rings[0].set_update("manual") + self.progressbar.rings[1].set_update("manual") self.progressbar.rings[2].set_update("scan") # Set the values of the rings to 50, 75, and 25 from outer to inner ring # self.progressbar.set_value([50, 75]) @@ -124,12 +126,12 @@ class flomniGuiTools: def _flomnigui_update_progress(self): if self.progressbar is not None: progress = self.progress["projection"] / self.progress["total_projections"] * 100 - subotmo_progress = ( + subtomo_progress = ( self.progress["subtomo_projection"] / self.progress["subtomo_total_projections"] * 100 ) - self.progressbar.set_value([progress, subotmo_progress]) + self.progressbar.set_value([progress, subtomo_progress, 0]) text = f"Progress report:\n Tomo type: ....................... {self.progress['tomo_type']}\n Projection: ...................... {self.progress['projection']:.0f}\n Total projections expected ....... {self.progress['total_projections']}\n Angle: ........................... {self.progress['angle']}\n Current subtomo: ................. {self.progress['subtomo']}\n Current projection within subtomo: {self.progress['subtomo_projection']}\n Total projections per subtomo: ... {self.progress['subtomo_total_projections']}" self.text_box.set_plain_text(text) diff --git a/csaxs_bec/devices/omny/webcam_viewer.py b/csaxs_bec/devices/omny/webcam_viewer.py index 3bfecb2..a15b1fd 100644 --- a/csaxs_bec/devices/omny/webcam_viewer.py +++ b/csaxs_bec/devices/omny/webcam_viewer.py @@ -11,7 +11,7 @@ from bec_lib.logger import bec_logger logger = bec_logger.logger class WebcamViewer(Device): - USER_ACCESS = ["start", "stop"] + USER_ACCESS = ["start_live_mode", "stop_live_mode"] preview = Cpt(PreviewSignal, ndim=2, num_rotation_90=0, transpose=False) def __init__(self, url:str, name:str, num_rotation_90=0, transpose=False, **kwargs) -> None: @@ -24,7 +24,7 @@ class WebcamViewer(Device): self.preview.num_rotation_90 = num_rotation_90 self.preview.transpose = transpose - def start(self) -> None: + def start_live_mode(self) -> None: if self._connection is not None: return self._update_thread = threading.Thread(target=self._update_loop, daemon=True) @@ -51,7 +51,7 @@ class WebcamViewer(Device): content = traceback.format_exc() logger.error(f"Image update loop failed: {content}") - def stop(self) -> None: + def stop_live_mode(self) -> None: if self._connection is None: return self._shutdown_event.set() -- 2.49.1 From b08c7bf44b149b09312a6c48da4a5d6025d93f7e Mon Sep 17 00:00:00 2001 From: x01dc Date: Wed, 8 Oct 2025 14:21:39 +0200 Subject: [PATCH 5/6] removed debug info --- csaxs_bec/scans/flomni_fermat_scan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csaxs_bec/scans/flomni_fermat_scan.py b/csaxs_bec/scans/flomni_fermat_scan.py index 28528fa..045ef1e 100644 --- a/csaxs_bec/scans/flomni_fermat_scan.py +++ b/csaxs_bec/scans/flomni_fermat_scan.py @@ -169,7 +169,7 @@ class FlomniFermatScan(SyncFlyScanBase): tracker_signal_status = yield from self.stubs.send_rpc_and_wait( "rtx", "controller.laser_tracker_check_signalstrength" ) - self.device_manager.connector.send_client_info(tracker_signal_status) + #self.device_manager.connector.send_client_info(tracker_signal_status) if tracker_signal_status == "low": self.device_manager.connector.raise_alarm( severity=0, -- 2.49.1 From 1fcb2133366c0cc49091e89d08094bc03a2fd374 Mon Sep 17 00:00:00 2001 From: wakonig_k Date: Wed, 8 Oct 2025 16:33:34 +0200 Subject: [PATCH 6/6] ci: pull from github instead of gitea --- .gitea/workflows/ci.yml | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 4573e56..b2cbee0 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -47,25 +47,13 @@ jobs: python-version: "${{ inputs.PYTHON_VERSION || '3.11' }}" - name: Checkout BEC Core - uses: actions/checkout@v4 - with: - repository: bec/bec - ref: "${{ inputs.BEC_CORE_BRANCH || 'main' }}" - path: ./bec + run: git clone --depth 1 --branch "${{ inputs.BEC_CORE_BRANCH || 'main' }}" https://github.com/bec-project/bec.git ./bec - name: Checkout Ophyd Devices - uses: actions/checkout@v4 - with: - repository: bec/ophyd_devices - ref: "${{ inputs.OPHYD_DEVICES_BRANCH || 'main' }}" - path: ./ophyd_devices + run: git clone --depth 1 --branch "${{ inputs.OPHYD_DEVICES_BRANCH || 'main' }}" https://github.com/bec-project/ophyd_devices.git ./ophyd_devices - name: Checkout BEC Widgets - uses: actions/checkout@v4 - with: - repository: bec/bec_widgets - ref: "${{ inputs.BEC_WIDGETS_BRANCH || 'main' }}" - path: ./bec_widgets + run: git clone --depth 1 --branch "${{ inputs.BEC_WIDGETS_BRANCH || 'main' }}" https://github.com/bec-project/bec_widgets.git ./bec_widgets - name: Checkout BEC Plugin Repository uses: actions/checkout@v4 -- 2.49.1