diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py index 87f3680..1ffeea8 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/flomni.py @@ -1004,6 +1004,20 @@ class FlomniAlignmentMixin: with open(os.path.join(dir_path, "ptychotomoalign_Cy3.txt"), "r") as file: tomo_alignment_fit[1][4] = file.readline() + tomo_alignment_fit[0][0] = gui.flomni.xeyegui.XRayEye.Waveform.fit_x_SineModel.dap_params['amplitude'] + tomo_alignment_fit[0][1] = gui.flomni.xeyegui.XRayEye.Waveform.fit_x_SineModel.dap_params['frequency'] + tomo_alignment_fit[0][2] = gui.flomni.xeyegui.XRayEye.Waveform.fit_x_SineModel.dap_params['shift'] + tomo_alignment_fit[1][0] = gui.flomni.xeyegui.XRayEye.Waveform_0.fit_y_SineModel.dap_params['amplitude'] + tomo_alignment_fit[1][1] = gui.flomni.xeyegui.XRayEye.Waveform_0.fit_y_SineModel.dap_params['frequency'] + tomo_alignment_fit[1][2] = gui.flomni.xeyegui.XRayEye.Waveform_0.fit_y_SineModel.dap_params['shift'] + print("applying vertical default values from mirror calibration, not from fit!") + tomo_alignment_fit[1][0] = 0 + tomo_alignment_fit[1][1] = 0 + tomo_alignment_fit[1][2] = 0 + tomo_alignment_fit[1][3] = 0 + tomo_alignment_fit[1][4] = 0 + + print("New alignment parameters loaded:") print( f"X Amplitude {tomo_alignment_fit[0][0]}, " 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 1d93592..2f0491d 100644 --- a/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py +++ b/csaxs_bec/bec_ipython_client/plugins/flomni/gui_tools.py @@ -1,6 +1,6 @@ import builtins -from bec_widgets.cli.client import BECDockArea +from bec_widgets.cli.client import AdvancedDockArea # from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose @@ -26,10 +26,11 @@ class flomniGuiTools: 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.new("flomni") + # if "flomni" in self.gui.windows: + # self.gui.flomni.show() + # else: + # self.gui.new("flomni") def flomnigui_stop_gui(self): self.gui.flomni.hide() @@ -37,24 +38,23 @@ class flomniGuiTools: def flomnigui_raise(self): self.gui.flomni.raise_window() - # def flomnigui_show_xeyealign(self): - # self.flomnigui_show_gui() - # if self.xeyegui is None: - # self.flomnigui_remove_all_docks() - # self.xeyegui = self.gui.flomni.new("xeyegui").new("XRayEye") - # # start live - # if not dev.cam_xeye.live_mode: - # dev.cam_xeye.live_mode = True - def flomnigui_show_xeyealign(self): self.flomnigui_show_gui() if self._flomnigui_check_attribute_not_exists("xeyegui"): self.flomnigui_remove_all_docks() - self.xeyegui = self.gui.flomni.new("xeyegui").new("XRayEye") - # start live - if not dev.cam_xeye.live_mode: - dev.cam_xeye.live_mode = True + self.xeyegui = self.gui.flomni.new("XRayEye",object_name='xrayeye') + # start live + if not dev.cam_xeye.live_mode_enabled.get(): + # dev.cam_xeye.live_mode = True + dev.cam_xeye.live_mode_enabled.put(True) + self.xeyegui.switch_tab('alignment') + def flomnigui_show_xeyealign_fittab(self): + self.flomnigui_show_gui() + if self._flomnigui_check_attribute_not_exists("xeyegui"): + self.flomnigui_remove_all_docks() + self.xeyegui = self.gui.flomni.new("XRayEye") + self.xeyegui.switch_tab('fit') def _flomnigui_check_attribute_not_exists(self, attribute_name): if hasattr(self.gui,"flomni"): @@ -67,7 +67,7 @@ class flomniGuiTools: self.flomnigui_show_gui() if self._flomnigui_check_attribute_not_exists("camera_gripper") or self._flomnigui_check_attribute_not_exists("camera_overview"): self.flomnigui_remove_all_docks() - camera_gripper_image = self.gui.flomni.new("camera_gripper").new("Image") + camera_gripper_image = self.gui.flomni.new("Image") if self._flomnicam_check_device_exists(dev.cam_flomni_gripper): camera_gripper_image.image(("cam_flomni_gripper", "preview")) camera_gripper_image.lock_aspect_ratio = True @@ -78,7 +78,7 @@ class flomniGuiTools: dev.cam_flomni_gripper.start_live_mode() else: print("Cannot open camera_gripper. Device does not exist.") - camera_overview_image = self.gui.flomni.new("camera_overview").new("Image") + camera_overview_image = self.gui.flomni.new("Image") if self._flomnicam_check_device_exists(dev.cam_flomni_overview): camera_overview_image.image(("cam_flomni_overview", "preview")) camera_overview_image.lock_aspect_ratio = True @@ -102,7 +102,7 @@ class flomniGuiTools: self.flomnigui_show_gui() if self._flomnigui_check_attribute_not_exists("idle_text_box"): self.flomnigui_remove_all_docks() - idle_text_box = self.gui.flomni.new("idle_textbox").new("TextBox") + idle_text_box = self.gui.flomni.new("TextBox") text = ( "
"
+ " ,---.,--. ,-----. ,--. ,--.,--. ,--.,--. \n"
@@ -162,7 +162,7 @@ class flomniGuiTools:
self.pdf_viewer = self.gui.flomni.new(widget="PdfViewerWidget")
# --- Load PDF ---------------------------------------------------------
- self.pdf_viewer.PdfViewerWidget.load_pdf(str(pdf_file.resolve()))
+ self.PdfViewerWidget.load_pdf(str(pdf_file.resolve()))
print(f"\nLoaded: {pdf_file.name}\n")
@@ -179,7 +179,7 @@ class flomniGuiTools:
if self._flomnigui_check_attribute_not_exists("progressbar"):
self.flomnigui_remove_all_docks()
# Add a new dock with a RingProgressBar widget
- self.progressbar = self.gui.flomni.new("progressbar").new("RingProgressBar")
+ self.progressbar = self.gui.flomni.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
@@ -194,7 +194,7 @@ 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.flomni.new(name="progress_text").new("TextBox")
+ self.text_box = self.gui.flomni.new("TextBox")
self._flomnigui_update_progress()
diff --git a/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py b/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py
index f156aad..b2058c5 100644
--- a/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py
+++ b/csaxs_bec/bec_ipython_client/plugins/flomni/x_ray_eye_align.py
@@ -5,9 +5,11 @@ import os
import time
from typing import TYPE_CHECKING
+import numpy as np
+
from bec_lib import bec_logger
-from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose
+# from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose
logger = bec_logger.logger
# import builtins to avoid linter errors
@@ -22,7 +24,7 @@ if TYPE_CHECKING:
class XrayEyeAlign:
# pixel calibration, multiply to get mm
- labview=False
+ test_wo_movements = True
PIXEL_CALIBRATION = 0.1 / 113 # .2 with binning
def __init__(self, client, flomni: Flomni) -> None:
@@ -34,209 +36,194 @@ class XrayEyeAlign:
self.flomni.reset_correction()
self.flomni.reset_tomo_alignment_fit()
+ @property
+ def gui(self):
+ return self.flomni.xeyegui
+
def _reset_init_values(self):
self.shift_xy = [0, 0]
self._xray_fov_xy = [0, 0]
- def save_frame(self):
- epics_put("XOMNYI-XEYE-SAVFRAME:0", 1)
+ def update_frame(self, keep_shutter_open=False):
- def update_frame(self,keep_shutter_open=False):
- if self.labview:
- epics_put("XOMNYI-XEYE-ACQDONE:0", 0)
-
- if not self.labview:
- self.flomni.flomnigui_show_xeyealign()
- if not dev.cam_xeye.live_mode:
- dev.cam_xeye.live_mode = True
+ # self.flomni.flomnigui_show_xeyealign()
+ if not dev.cam_xeye.live_mode_enabled.get():
+ dev.cam_xeye.live_mode_enabled.put(True)
- epics_put("XOMNYI-XEYE-ACQ:0", 1)
- if self.labview:
- # wait for start live
- while epics_get("XOMNYI-XEYE-ACQDONE:0") == 0:
- time.sleep(0.5)
- print("waiting for live view to start...")
+ self.gui.on_live_view_enabled(True)
- fshopen()
-
- if self.labview:
- epics_put("XOMNYI-XEYE-ACQDONE:0", 0)
-
- while epics_get("XOMNYI-XEYE-ACQDONE:0") == 0:
- print("waiting for new frame...")
- time.sleep(0.5)
+ dev.omnyfsh.fshopen()
time.sleep(0.5)
# stop live view
if not keep_shutter_open:
- epics_put("XOMNYI-XEYE-ACQ:0", 0)
+ self.gui.on_live_view_enabled(False)
time.sleep(0.1)
- fshclose()
- print("got new frame")
+ dev.omnyfsh.fshclose()
+ print("Received new frame.")
else:
print("Staying in live view, shutter is and remains open!")
def tomo_rotate(self, val: float):
- # pylint: disable=undefined-variable
- umv(self.device_manager.devices.fsamroy, val)
+ if not self.test_wo_movements:
+ umv(self.device_manager.devices.fsamroy, val)
def get_tomo_angle(self):
return self.device_manager.devices.fsamroy.readback.get()
def update_fov(self, k: int):
- self._xray_fov_xy[0] = max(epics_get(f"XOMNYI-XEYE-XWIDTH_X:{k}"), self._xray_fov_xy[0])
- self._xray_fov_xy[1] = max(0, self._xray_fov_xy[0])
+ self._xray_fov_xy[0] = max(
+ getattr(dev.omny_xray_gui, f"width_x_{k}").get(), self._xray_fov_xy[0]
+ )
+ self._xray_fov_xy[1] = max(
+ getattr(dev.omny_xray_gui, f"width_y_{k}").get(), self._xray_fov_xy[1]
+ )
- @property
- def movement_buttons_enabled(self):
- return [epics_get("XOMNYI-XEYE-ENAMVX:0"), epics_get("XOMNYI-XEYE-ENAMVY:0")]
-
- @movement_buttons_enabled.setter
- def movement_buttons_enabled(self, enabled: bool):
- enabled = int(enabled)
- epics_put("XOMNYI-XEYE-ENAMVX:0", enabled)
- epics_put("XOMNYI-XEYE-ENAMVY:0", enabled)
+ def movement_buttons_enabled(self, enablex: bool, enabley: bool):
+ self.gui.on_motors_enable(enablex, enabley)
def send_message(self, msg: str):
- epics_put("XOMNYI-XEYE-MESSAGE:0.DESC", msg)
+ print(f"In alginment GUI: {msg}")
+ self.gui.user_message = msg
- def align(self,keep_shutter_open=False):
+ def align(self, keep_shutter_open=False):
+ self.flomni.flomnigui_show_xeyealign()
if not keep_shutter_open:
- print("This routine can be called with paramter keep_shutter_open=True to keep the shutter always open")
+ print(
+ "This routine can be called with paramter keep_shutter_open=True to keep the shutter always open"
+ )
self.send_message("Getting things ready. Please wait...")
- #potential unresolved movement requests to zero
- epics_put("XOMNYI-XEYE-MVX:0", 0)
- epics_put("XOMNYI-XEYE-MVY:0", 0)
+ self.gui.enable_submit_button(False)
+
+ # Initialize xray align device
+ # clear potential pending movement requests
+ dev.omny_xray_gui.mvx.set(0)
+ dev.omny_xray_gui.mvy.set(0)
+ # reset submit channel
+ dev.omny_xray_gui.submit.set(0)
+
+ self.movement_buttons_enabled(False, False)
# reset shift xy and fov params
self._reset_init_values()
self.flomni.lights_off()
- self.flomni.flomnigui_show_xeyealign()
- self.flomni.flomnigui_raise()
+ # self.flomni.flomnigui_show_xeyealign()
+ # self.flomni.flomnigui_raise()
- self.tomo_rotate(0)
- epics_put("XOMNYI-XEYE-ANGLE:0", 0)
+ if not self.test_wo_movements:
+ self.tomo_rotate(0)
- self.flomni.feye_in()
+ self.flomni.feye_in()
self.flomni.laser_tracker_on()
self.flomni.feedback_enable_with_reset()
# disable movement buttons
- self.movement_buttons_enabled = False
+ self.movement_buttons_enabled(False, False)
sample_name = self.flomni.sample_get_name(0)
- epics_put("XOMNYI-XEYE-SAMPLENAME:0.DESC", sample_name)
+ self.gui.sample_name = sample_name
# this makes sure we are in a defined state
self.flomni.feedback_disable()
- epics_put("XOMNYI-XEYE-PIXELSIZE:0", self.PIXEL_CALIBRATION)
+ if not self.test_wo_movements:
+ self.flomni.fosa_out()
- self.flomni.fosa_out()
+ fsamx_in = self.flomni._get_user_param_safe("fsamx", "in")
+ umv(dev.fsamx, fsamx_in - 0.25)
- fsamx_in = self.flomni._get_user_param_safe("fsamx", "in")
- umv(dev.fsamx, fsamx_in - 0.25)
+ self.flomni.ffzp_in()
- self.flomni.ffzp_in()
self.update_frame(keep_shutter_open)
- # enable submit buttons
- self.movement_buttons_enabled = False
- epics_put("XOMNYI-XEYE-SUBMIT:0", 0)
- epics_put("XOMNYI-XEYE-STEP:0", 0)
+ self.gui.enable_submit_button(True)
+ dev.omny_xray_gui.step.set(0).wait()
self.send_message("Submit center value of FZP.")
k = 0
while True:
- if epics_get("XOMNYI-XEYE-SUBMIT:0") == 1:
- val_x = epics_get(f"XOMNYI-XEYE-XVAL_X:{k}") / 2 * self.PIXEL_CALIBRATION # in mm
- self.alignment_values[k] = val_x
+ if dev.omny_xray_gui.submit.get() == 1:
+
+ self.alignment_values[k] = (
+ getattr(dev.omny_xray_gui, f"xval_x_{k}").get() / 2 * self.PIXEL_CALIBRATION
+ ) # in mm
print(f"Clicked position {k}: x {self.alignment_values[k]}")
rtx_position = dev.rtx.readback.get() / 1000
print(f"Current rtx position {rtx_position}")
self.alignment_values[k] -= rtx_position
print(f"Corrected position {k}: x {self.alignment_values[k]}")
-
+ # reset submit channel
+ dev.omny_xray_gui.submit.set(0)
if k == 0: # received center value of FZP
self.send_message("please wait ...")
- self.movement_buttons_enabled = False
- epics_put("XOMNYI-XEYE-SUBMIT:0", -1) # disable submit button
+ self.movement_buttons_enabled(False, False)
+ self.gui.enable_submit_button(False)
self.flomni.feedback_disable()
- fsamx_in = self.flomni._get_user_param_safe("fsamx", "in")
- umv(dev.fsamx, fsamx_in)
+ if not self.test_wo_movements:
+ fsamx_in = self.flomni._get_user_param_safe("fsamx", "in")
+ umv(dev.fsamx, fsamx_in)
- self.flomni.foptics_out()
+ self.flomni.foptics_out()
- self.flomni.feedback_disable()
- umv(dev.fsamx, fsamx_in - 0.25)
+ time.sleep(0.5)
- if self.labview:
- self.update_frame(keep_shutter_open)
- epics_put("XOMNYI-XEYE-RECBG:0", 1)
- while epics_get("XOMNYI-XEYE-RECBG:0") == 1:
- time.sleep(0.5)
- print("waiting for background frame...")
-
- umv(dev.fsamx, fsamx_in)
- time.sleep(0.5)
self.flomni.feedback_enable_with_reset()
self.update_frame(keep_shutter_open)
- self.send_message("Adjust sample height and submit center")
- epics_put("XOMNYI-XEYE-SUBMIT:0", 0)
- self.movement_buttons_enabled = True
+ self.send_message("Step 1/5: Adjust sample height and submit center")
+ self.gui.enable_submit_button(True)
+ self.movement_buttons_enabled(True, True)
elif 1 <= k < 5: # received sample center value at samroy 0 ... 315
self.send_message("please wait ...")
- epics_put("XOMNYI-XEYE-SUBMIT:0", -1)
- self.movement_buttons_enabled = False
+ self.gui.enable_submit_button(False)
+ self.movement_buttons_enabled(False, False)
umv(dev.rtx, 0)
self.tomo_rotate(k * 45)
- epics_put("XOMNYI-XEYE-ANGLE:0", self.get_tomo_angle())
+ dev.omny_xray_gui.angle.set(self.get_tomo_angle())
self.update_frame(keep_shutter_open)
- self.send_message("Submit sample center")
- epics_put("XOMNYI-XEYE-SUBMIT:0", 0)
- epics_put("XOMNYI-XEYE-ENAMVX:0", 1)
+ self.send_message(f"Step {k+1}/5: Submit sample center")
+ self.gui.enable_submit_button(True)
+ self.movement_buttons_enabled(True, False)
self.update_fov(k)
elif k == 5: # received sample center value at samroy 270 and done
self.send_message("done...")
- epics_put("XOMNYI-XEYE-SUBMIT:0", -1) # disable submit button
- self.movement_buttons_enabled = False
+ self.gui.enable_submit_button(False)
+ self.movement_buttons_enabled(False, False)
self.update_fov(k)
break
k += 1
- epics_put("XOMNYI-XEYE-STEP:0", k)
+ dev.omny_xray_gui.step.set(k)
- _xrayeyalignmvx = epics_get("XOMNYI-XEYE-MVX:0")
+ _xrayeyalignmvx = dev.omny_xray_gui.mvx.get()
if _xrayeyalignmvx != 0:
umvr(dev.rtx, _xrayeyalignmvx)
print(f"Current rtx position {dev.rtx.readback.get() / 1000}")
- epics_put("XOMNYI-XEYE-MVX:0", 0)
- if k > 0:
- epics_put(f"XOMNYI-XEYE-STAGEPOSX:{k}", dev.rtx.readback.get() / 1000)
time.sleep(3)
+ dev.omny_xray_gui.mvx.set(0)
self.update_frame(keep_shutter_open)
if k < 2:
# allow movements, store movements to calculate center
- _xrayeyalignmvy = epics_get("XOMNYI-XEYE-MVY:0")
+ _xrayeyalignmvy = dev.omny_xray_gui.mvy.get()
if _xrayeyalignmvy != 0:
self.flomni.feedback_disable()
- umvr(dev.fsamy, _xrayeyalignmvy / 1000)
+ if not self.test_wo_movements:
+ umvr(dev.fsamy, _xrayeyalignmvy / 1000)
time.sleep(2)
- epics_put("XOMNYI-XEYE-MVY:0", 0)
+ dev.omny_xray_gui.mvy.set(0)
self.flomni.feedback_enable_with_reset()
self.update_frame(keep_shutter_open)
- time.sleep(0.2)
+ time.sleep(0.1)
self.write_output()
fovx = self._xray_fov_xy[0] * self.PIXEL_CALIBRATION * 1000 / 2
@@ -246,22 +233,17 @@ class XrayEyeAlign:
umv(dev.rtx, 0)
- # free camera
- if self.labview:
- epics_put("XOMNYI-XEYE-ACQ:0", 2)
- if keep_shutter_open and not self.labview:
- if self.flomni.OMNYTools.yesno("Close the shutter now?","y"):
- fshclose()
- epics_put("XOMNYI-XEYE-ACQ:0", 0)
- if not self.labview:
- self.flomni.flomnigui_idle()
-
+ if keep_shutter_open:
+ if self.flomni.OMNYTools.yesno("Close the shutter now?", "y"):
+ dev.omnyfsh.fshclose()
+ self.gui.on_live_view_enabled(False)
+ print("setting 'XOMNYI-XEYE-ACQ:0'")
print(
f"The largest field of view from the xrayeyealign was \nfovx = {fovx:.0f} microns, fovy"
f" = {fovy:.0f} microns"
)
- print("Use the matlab routine to FIT the current alignment...")
+ print("Check the fit in the GUI...")
print("Then LOAD ALIGNMENT PARAMETERS by running flomni.read_alignment_offset()\n")
@@ -269,9 +251,33 @@ class XrayEyeAlign:
file = os.path.expanduser("~/Data10/specES1/internal/xrayeye_alignmentvalues")
if not os.path.exists(file):
os.makedirs(os.path.dirname(file), exist_ok=True)
+
with open(file, "w") as alignment_values_file:
alignment_values_file.write("angle\thorizontal\n")
+
+ # Initialize an empty list to store fovx values
+ fovx_list = []
+ fovx_offsets = np.zeros(5) # holds offsets for k = 1..5
+
for k in range(1, 6):
fovx_offset = self.alignment_values[0] - self.alignment_values[k]
+ fovx_offsets[k - 1] = fovx_offset # store in array
+
+ fovx_x = (k - 1) * 45
+ fovx_list.append([fovx_x, fovx_offset * 1000]) # Append the data to the list
+
print(f"Writing to file new alignment: number {k}, value x {fovx_offset}")
- alignment_values_file.write(f"{(k-1)*45}\t{fovx_offset*1000}\n")
+ alignment_values_file.write(f"{fovx_x}\t{fovx_offset * 1000}\n")
+
+ # Now build final numpy array:
+ data = np.array(
+ [
+ [0, 45, 90, 135, 180], # angles
+ fovx_offsets * 1000, # fovx_offset values
+ [0, 0, 0, 0, 0],
+ ]
+ )
+ self.gui.submit_fit_array(data)
+ print(f"fit submited with {data}")
+ print("todo mirko: submitted data is 1000 fold in amplitude")
+ # self.flomni.flomnigui_show_xeyealign_fittab()
diff --git a/csaxs_bec/bec_ipython_client/plugins/omny/gui_tools.py b/csaxs_bec/bec_ipython_client/plugins/omny/gui_tools.py
index 2ce910f..8a064d4 100644
--- a/csaxs_bec/bec_ipython_client/plugins/omny/gui_tools.py
+++ b/csaxs_bec/bec_ipython_client/plugins/omny/gui_tools.py
@@ -1,6 +1,6 @@
import builtins
-from bec_widgets.cli.client import BECDockArea
+from bec_widgets.cli.client import AdvancedDockArea
# from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose
diff --git a/csaxs_bec/bec_widgets/widgets/client.py b/csaxs_bec/bec_widgets/widgets/client.py
index ec2d37c..82a56c0 100644
--- a/csaxs_bec/bec_widgets/widgets/client.py
+++ b/csaxs_bec/bec_widgets/widgets/client.py
@@ -83,20 +83,6 @@ class XRayEye(RPCBase):
Return the currently active ROI, or None if no ROI is active.
"""
- @property
- @rpc_call
- def enable_live_view(self):
- """
- Get or set the live view enabled state.
- """
-
- @enable_live_view.setter
- @rpc_call
- def enable_live_view(self):
- """
- Get or set the live view enabled state.
- """
-
@property
@rpc_call
def user_message(self):
@@ -111,6 +97,30 @@ class XRayEye(RPCBase):
None
"""
+ @rpc_call
+ def on_live_view_enabled(self, enabled: "bool"):
+ """
+ None
+ """
+
+ @rpc_call
+ def on_motors_enable(self, x_enable: "bool", y_enable: "bool"):
+ """
+ Enable/Disable motor controls
+
+ Args:
+ x_enable(bool): enable x motor controls
+ y_enable(bool): enable y motor controls
+ """
+
+ @rpc_call
+ def enable_submit_button(self, enable: "int"):
+ """
+ Enable/disable submit button.
+ Args:
+ enable(int): -1 disable else enable
+ """
+
@property
@rpc_call
def sample_name(self):
@@ -139,6 +149,18 @@ class XRayEye(RPCBase):
None
"""
+ @rpc_call
+ def switch_tab(self, tab: "str"):
+ """
+ None
+ """
+
+ @rpc_call
+ def submit_fit_array(self, fit_array):
+ """
+ None
+ """
+
class XRayEye2DControl(RPCBase):
@rpc_call
diff --git a/csaxs_bec/bec_widgets/widgets/omny_alignment/__init__.py b/csaxs_bec/bec_widgets/widgets/omny_alignment/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment.py b/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment.py
deleted file mode 100644
index 622bf3c..0000000
--- a/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment.py
+++ /dev/null
@@ -1,140 +0,0 @@
-
-
-from typing import TypedDict
-from bec_widgets.utils.error_popups import SafeSlot
-import os
-from bec_widgets.utils.bec_widget import BECWidget
-from bec_widgets.utils.ui_loader import UILoader
-from qtpy.QtWidgets import QWidget, QPushButton, QLineEdit, QLabel, QVBoxLayout
-from bec_qthemes import material_icon
-from bec_lib.logger import bec_logger
-
-logger = bec_logger.logger
-
-
-# class OmnyAlignmentUIComponents(TypedDict):
-# moveRightButton: QPushButton
-# moveLeftButton: QPushButton
-# moveUpButton: QPushButton
-# moveDownButton: QPushButton
-# image: Image
-
-
-class OmnyAlignment(BECWidget, QWidget):
- USER_ACCESS = ["enable_live_view", "enable_live_view.setter", "user_message", "user_message.setter","sample_name", "sample_name.setter", "enable_move_buttons", "enable_move_buttons.setter"]
- PLUGIN = True
- ui_file = "./omny_alignment.ui"
-
- def __init__(self, parent=None, **kwargs):
- super().__init__(parent=parent, **kwargs)
-
- self._load_ui()
-
- def _load_ui(self):
- current_path = os.path.dirname(__file__)
- self.ui = UILoader(self).loader(os.path.join(current_path, self.ui_file))
- layout = QVBoxLayout()
- layout.addWidget(self.ui)
- self.setLayout(layout)
-
- icon_options = {"size": (16, 16), "convert_to_pixmap": False}
- self.ui.moveRightButton.setText("")
- self.ui.moveRightButton.setIcon(
- material_icon(icon_name="keyboard_arrow_right", **icon_options)
- )
-
- self.ui.moveLeftButton.setText("")
- self.ui.moveLeftButton.setIcon(
- material_icon(icon_name="keyboard_arrow_left", **icon_options)
- )
-
- self.ui.moveUpButton.setText("")
- self.ui.moveUpButton.setIcon(
- material_icon(icon_name="keyboard_arrow_up", **icon_options)
- )
-
- self.ui.moveDownButton.setText("")
- self.ui.moveDownButton.setIcon(
- material_icon(icon_name="keyboard_arrow_down", **icon_options)
- )
-
- self.ui.confirmButton.setText("OK")
-
-
- self.ui.liveViewSwitch.enabled.connect(self.on_live_view_enabled)
-
- # self.ui.moveUpButton.clicked.connect(self.on_move_up)
-
-
- @property
- def enable_live_view(self):
- return self.ui.liveViewSwitch.checked
-
- @enable_live_view.setter
- def enable_live_view(self, enable:bool):
- self.ui.liveViewSwitch.checked = enable
-
-
- @property
- def user_message(self):
- return self.ui.messageLineEdit.text()
-
- @user_message.setter
- def user_message(self, message:str):
- self.ui.messageLineEdit.setText(message)
-
- @property
- def sample_name(self):
- return self.ui.sampleLineEdit.text()
-
- @sample_name.setter
- def sample_name(self, message:str):
- self.ui.sampleLineEdit.setText(message)
-
-
- @SafeSlot(bool)
- def on_live_view_enabled(self, enabled:bool):
- from bec_widgets.widgets.plots.image.image import Image
- logger.info(f"Live view is enabled: {enabled}")
- image: Image = self.ui.image
- if enabled:
- image.image("cam_xeye")
- return
-
- image.disconnect_monitor("cam_xeye")
-
-
- @property
- def enable_move_buttons(self):
- move_up:QPushButton = self.ui.moveUpButton
- move_down:QPushButton = self.ui.moveDownButton
- move_left:QPushButton = self.ui.moveLeftButton
- move_right:QPushButton = self.ui.moveRightButton
- return move_up.isEnabled() and move_down.isEnabled() and move_left.isEnabled() and move_right.isEnabled()
-
- @enable_move_buttons.setter
- def enable_move_buttons(self, enabled:bool):
- move_up:QPushButton = self.ui.moveUpButton
- move_down:QPushButton = self.ui.moveDownButton
- move_left:QPushButton = self.ui.moveLeftButton
- move_right:QPushButton = self.ui.moveRightButton
-
- move_up.setEnabled(enabled)
- move_down.setEnabled(enabled)
- move_left.setEnabled(enabled)
- move_right.setEnabled(enabled)
-
-
-
-
-
-
-if __name__ == "__main__":
- from qtpy.QtWidgets import QApplication
- import sys
-
- app = QApplication(sys.argv)
- widget = OmnyAlignment()
-
- widget.show()
- sys.exit(app.exec_())
\ No newline at end of file
diff --git a/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment.pyproject b/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment.pyproject
deleted file mode 100644
index 3182247..0000000
--- a/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment.pyproject
+++ /dev/null
@@ -1 +0,0 @@
-{'files': ['omny_alignment.py']}
\ No newline at end of file
diff --git a/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment.ui b/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment.ui
deleted file mode 100644
index 6572d2f..0000000
--- a/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment.ui
+++ /dev/null
@@ -1,125 +0,0 @@
-
-
- Form
-
-
-
- 0
- 0
- 988
- 821
-
-
-
- Form
-
-
- -
-
- -
-
-
- PushButton
-
-
-
- -
-
-
- PushButton
-
-
-
- -
-
-
- Up
-
-
-
- -
-
-
- PushButton
-
-
-
- -
-
-
- PushButton
-
-
-
-
-
- -
-
- -
-
-
- -
-
-
- -
-
-
- Sample
-
-
-
- -
-
-
- Message
-
-
-
-
-
- -
-
-
- false
-
-
- false
-
-
- cam_xeye
-
-
- 3
-
-
-
- -
-
-
- -
-
-
- Live View
-
-
- Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter
-
-
-
-
-
-
-
- Image
- QWidget
- image
-
-
- ToggleSwitch
- QWidget
- toggle_switch
-
-
-
-
-
diff --git a/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment_plugin.py b/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment_plugin.py
deleted file mode 100644
index cbbd81b..0000000
--- a/csaxs_bec/bec_widgets/widgets/omny_alignment/omny_alignment_plugin.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-from qtpy.QtDesigner import QDesignerCustomWidgetInterface
-
-from bec_widgets.utils.bec_designer import designer_material_icon
-from csaxs_bec.bec_widgets.widgets.omny_alignment.omny_alignment import OmnyAlignment
-
-DOM_XML = """
-
-
-
-
-"""
-
-
-class OmnyAlignmentPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
- def __init__(self):
- super().__init__()
- self._form_editor = None
-
- def createWidget(self, parent):
- t = OmnyAlignment(parent)
- return t
-
- def domXml(self):
- return DOM_XML
-
- def group(self):
- return ""
-
- def icon(self):
- return designer_material_icon(OmnyAlignment.ICON_NAME)
-
- def includeFile(self):
- return "omny_alignment"
-
- def initialize(self, form_editor):
- self._form_editor = form_editor
-
- def isContainer(self):
- return False
-
- def isInitialized(self):
- return self._form_editor is not None
-
- def name(self):
- return "OmnyAlignment"
-
- def toolTip(self):
- return "OmnyAlignment"
-
- def whatsThis(self):
- return self.toolTip()
diff --git a/csaxs_bec/bec_widgets/widgets/omny_alignment/register_omny_alignment.py b/csaxs_bec/bec_widgets/widgets/omny_alignment/register_omny_alignment.py
deleted file mode 100644
index 701fb7c..0000000
--- a/csaxs_bec/bec_widgets/widgets/omny_alignment/register_omny_alignment.py
+++ /dev/null
@@ -1,15 +0,0 @@
-def main(): # pragma: no cover
- from qtpy import PYSIDE6
-
- if not PYSIDE6:
- print("PYSIDE6 is not available in the environment. Cannot patch designer.")
- return
- from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
-
- from csaxs_bec.bec_widgets.widgets.omny_alignment.omny_alignment_plugin import OmnyAlignmentPlugin
-
- QPyDesignerCustomWidgetCollection.addCustomWidget(OmnyAlignmentPlugin())
-
-
-if __name__ == "__main__": # pragma: no cover
- main()
diff --git a/csaxs_bec/bec_widgets/widgets/xray_eye/x_ray_eye.py b/csaxs_bec/bec_widgets/widgets/xray_eye/x_ray_eye.py
index 2ba48e7..78d516b 100644
--- a/csaxs_bec/bec_widgets/widgets/xray_eye/x_ray_eye.py
+++ b/csaxs_bec/bec_widgets/widgets/xray_eye/x_ray_eye.py
@@ -5,6 +5,7 @@ from bec_lib.endpoints import MessageEndpoints
from bec_qthemes import material_icon
from bec_widgets import BECWidget, SafeProperty, SafeSlot
from bec_widgets.widgets.plots.image.image import Image
+from bec_widgets.widgets.plots.waveform.waveform import Waveform
from bec_widgets.widgets.plots.image.setting_widgets.image_roi_tree import ROIPropertyTree
from bec_widgets.widgets.plots.roi.image_roi import BaseROI, CircularROI, RectangularROI
from bec_widgets.widgets.utility.toggle.toggle import ToggleSwitch
@@ -21,7 +22,10 @@ from qtpy.QtWidgets import (
QToolButton,
QVBoxLayout,
QWidget,
+ QTextEdit,
+ QTabWidget
)
+import time
logger = bec_logger.logger
CAMERA = ("cam_xeye", "image")
@@ -124,8 +128,8 @@ class XRayEye2DControl(BECWidget, QWidget):
class XRayEye(BECWidget, QWidget):
- USER_ACCESS = ["active_roi", "enable_live_view", "enable_live_view.setter", "user_message", "user_message.setter",
- "sample_name", "sample_name.setter", "enable_move_buttons", "enable_move_buttons.setter"]
+ USER_ACCESS = ["active_roi", "user_message", "user_message.setter","on_live_view_enabled","on_motors_enable","enable_submit_button",
+ "sample_name", "sample_name.setter", "enable_move_buttons", "enable_move_buttons.setter","switch_tab","submit_fit_array"]
PLUGIN = True
def __init__(self, parent=None, **kwargs):
@@ -136,21 +140,31 @@ class XRayEye(BECWidget, QWidget):
self._make_connections()
# Connection to redis endpoints
- self.bec_dispatcher.connect_slot(self.device_updates, MessageEndpoints.device_readback("omny_xray_gui"))
+ self.bec_dispatcher.connect_slot(self.getting_shutter_status, MessageEndpoints.device_readback("omnyfsh"))
+ self.bec_dispatcher.connect_slot(self.getting_camera_status, MessageEndpoints.device_read_configuration(CAMERA[0]))
+
self.connect_motors()
self.resize(800, 600)
QTimer.singleShot(0, self._init_gui_trigger)
def _init_ui(self):
- self.core_layout = QHBoxLayout(self)
+ self.root_layout = QVBoxLayout(self)
+ self.tab_widget = QTabWidget(parent=self)
+ self.root_layout.addWidget(self.tab_widget)
- self.image = Image(parent=self)
+ self.alignment_tab = QWidget(parent=self)
+ self.core_layout = QHBoxLayout(self.alignment_tab)
+
+ self.image = Image(parent=self.alignment_tab)
+ self.image.color_map = "CET-L2"
self.image.enable_toolbar = False # Disable default toolbar to not allow to user set anything
self.image.inner_axes = False # Disable inner axes to maximize image area
- self.image.plot_item.vb.invertY(True) # #TODO Invert y axis to match logic of LabView GUI
+ self.image.enable_full_colorbar = True
+ self.image.invert_y = True # Invert y axis to match image coordinates
+
# Control panel on the right: vertical layout inside a fixed-width widget
- self.control_panel = QWidget(parent=self)
+ self.control_panel = QWidget(parent=self.alignment_tab)
self.control_panel_layout = QVBoxLayout(self.control_panel)
self.control_panel_layout.setContentsMargins(0, 0, 0, 0)
self.control_panel_layout.setSpacing(10)
@@ -166,16 +180,35 @@ class XRayEye(BECWidget, QWidget):
self.live_preview_label = QLabel("Live Preview", parent=self)
self.live_preview_toggle = ToggleSwitch(parent=self)
self.live_preview_toggle.checked = False
- header_row.addWidget(self.live_preview_label, 0, Qt.AlignVCenter)
- header_row.addWidget(self.live_preview_toggle, 0, Qt.AlignVCenter)
+ header_row.addWidget(self.live_preview_label, 0, Qt.AlignmentFlag.AlignVCenter)
+ header_row.addWidget(self.live_preview_toggle, 0, Qt.AlignmentFlag.AlignVCenter)
self.control_panel_layout.addLayout(header_row)
+ switch_row = QHBoxLayout()
+ switch_row.setContentsMargins(0, 0, 0, 0)
+ switch_row.setSpacing(8)
+ switch_row.addStretch()
+ self.camera_running_label = QLabel("Camera running", parent=self)
+ self.camera_running_toggle = ToggleSwitch(parent=self)
+ # self.camera_running_toggle.checked = False
+ self.camera_running_toggle.enabled.connect(self.camera_running_enabled)
+ self.shutter_label = QLabel("Shutter open", parent=self)
+ self.shutter_toggle = ToggleSwitch(parent=self)
+ # self.shutter_toggle.checked = False
+ self.shutter_toggle.enabled.connect(self.opening_shutter)
+ switch_row.addWidget(self.shutter_label, 0, Qt.AlignmentFlag.AlignVCenter)
+ switch_row.addWidget(self.shutter_toggle, 0, Qt.AlignmentFlag.AlignVCenter)
+ switch_row.addWidget(self.camera_running_label, 0, Qt.AlignmentFlag.AlignVCenter)
+ switch_row.addWidget(self.camera_running_toggle, 0, Qt.AlignmentFlag.AlignVCenter)
+ self.control_panel_layout.addLayout(switch_row)
+
+
# separator
self.control_panel_layout.addWidget(self._create_separator())
# 2D Positioner (fixed size)
self.motor_control_2d = XRayEye2DControl(parent=self)
- self.control_panel_layout.addWidget(self.motor_control_2d, 0, Qt.AlignTop | Qt.AlignCenter)
+ self.control_panel_layout.addWidget(self.motor_control_2d, 0, Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignCenter)
# separator
self.control_panel_layout.addWidget(self._create_separator())
@@ -190,9 +223,8 @@ class XRayEye(BECWidget, QWidget):
# Submit button
self.submit_button = QPushButton("Submit", parent=self)
# Add to layout form
- step_size_form.addWidget(QLabel("Horizontal", parent=self), 0, 0)
+ step_size_form.addWidget(QLabel("Step Size", parent=self), 0, 0)
step_size_form.addWidget(self.step_size, 0, 1)
- step_size_form.addWidget(QLabel("Vertical", parent=self), 1, 0)
step_size_form.addWidget(self.submit_button, 2, 0, 1, 2)
# Add form to control panel
@@ -207,7 +239,8 @@ class XRayEye(BECWidget, QWidget):
self.sample_name_line_edit.setReadOnly(True)
form.addWidget(QLabel("Sample", parent=self), 0, 0)
form.addWidget(self.sample_name_line_edit, 0, 1)
- self.message_line_edit = QLineEdit(parent=self)
+ self.message_line_edit = QTextEdit(parent=self)
+ self.message_line_edit.setFixedHeight(60)
self.message_line_edit.setReadOnly(True)
form.addWidget(QLabel("Message", parent=self), 1, 0)
form.addWidget(self.message_line_edit, 1, 1)
@@ -217,12 +250,39 @@ class XRayEye(BECWidget, QWidget):
self.control_panel.adjustSize()
p_hint = self.control_panel.sizeHint()
self.control_panel.setFixedWidth(p_hint.width())
- self.control_panel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
+ self.control_panel.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding)
# Core Layout: image (expanding) | control panel (fixed)
self.core_layout.addWidget(self.image)
self.core_layout.addWidget(self.control_panel)
+ self.tab_widget.addTab(self.alignment_tab, "Alignment")
+
+ self.fit_tab = QWidget(parent=self)
+ self.fit_layout = QVBoxLayout(self.fit_tab)
+ self.waveform_x = Waveform(parent=self.fit_tab)
+ self.waveform_y = Waveform(parent=self.fit_tab)
+
+
+ self.waveform_x.plot(x=[0],y=[1], label="fit-x",dap="SineModel",dap_parameters={"frequency":{"value":0.0174533,"vary":False,"min":0.01,"max":0.02}},dap_oversample=5)
+ self.waveform_y.plot(x=[0],y=[2], label="fit-y",dap="SineModel")#,dap_oversample=5)
+ self.fit_x = self.waveform_x.curves[0]
+ self.fit_y = self.waveform_y.curves[0]
+
+ self.waveform_x.dap_params_update.connect(self.on_dap_params)
+ self.waveform_y.dap_params_update.connect(self.on_dap_params)
+
+
+ for wave in (self.waveform_x,self.waveform_y):
+ wave.x_label = "Angle (deg)"
+ wave.x_grid = True
+ wave.y_grid = True
+ wave.enable_toolbar = True
+
+ self.fit_layout.addWidget(self.waveform_x)
+ self.fit_layout.addWidget(self.waveform_y)
+ self.tab_widget.addTab(self.fit_tab, "Fit")
+
def _make_connections(self):
# Fetch initial state
self.on_live_view_enabled(True)
@@ -235,13 +295,14 @@ class XRayEye(BECWidget, QWidget):
def _create_separator(self):
sep = QFrame(parent=self)
- sep.setFrameShape(QFrame.HLine)
- sep.setFrameShadow(QFrame.Sunken)
+ sep.setFrameShape(QFrame.Shape.HLine)
+ sep.setFrameShadow(QFrame.Shadow.Sunken)
sep.setLineWidth(1)
return sep
def _init_gui_trigger(self):
self.dev.omny_xray_gui.read()
+ self.dev.omnyfsh.read()
################################################################################
# Device Connection logic
@@ -254,7 +315,7 @@ class XRayEye(BECWidget, QWidget):
for motor in possible_motors:
if motor in self.dev:
self.bec_dispatcher.connect_slot(self.on_tomo_angle_readback, MessageEndpoints.device_readback(motor))
- logger.info(f"Succesfully connected to {motor}")
+ logger.info(f"Successfully connected to {motor}")
################################################################################
# Properties ported from the original OmnyAlignment, can be adjusted as needed
@@ -290,6 +351,14 @@ class XRayEye(BECWidget, QWidget):
################################################################################
# Slots ported from the original OmnyAlignment, can be adjusted as needed
################################################################################
+
+ @SafeSlot(str)
+ def switch_tab(self,tab:str):
+ if tab == "fit":
+ self.tab_widget.setCurrentIndex(1)
+ else:
+ self.tab_widget.setCurrentIndex(0)
+
@SafeSlot()
def get_roi_coordinates(self) -> dict | None:
@@ -307,14 +376,50 @@ class XRayEye(BECWidget, QWidget):
self.live_preview_toggle.blockSignals(True)
if enabled:
self.live_preview_toggle.checked = enabled
- self.image.image(CAMERA)
+ self.image.image(device_name=CAMERA[0],device_entry=CAMERA[1])
self.live_preview_toggle.blockSignals(False)
return
- self.image.disconnect_monitor(CAMERA)
+ self.image.disconnect_monitor(CAMERA[0],CAMERA[1])
self.live_preview_toggle.checked = enabled
self.live_preview_toggle.blockSignals(False)
+ @SafeSlot(bool)
+ def camera_running_enabled(self, enabled: bool):
+ logger.info(f"Camera running: {enabled}")
+ self.camera_running_toggle.blockSignals(True)
+ self.dev.get(CAMERA[0]).live_mode_enabled.put(enabled)
+ self.camera_running_toggle.checked = enabled
+ self.camera_running_toggle.blockSignals(False)
+
+ @SafeSlot(dict,dict)
+ def getting_camera_status(self,data,meta):
+ print(f"msg:{data}")
+ live_mode_enabled = data.get("signals").get(f"{CAMERA[0]}_live_mode_enabled").get("value")
+ self.camera_running_toggle.blockSignals(True)
+ self.camera_running_toggle.checked = live_mode_enabled
+ self.camera_running_toggle.blockSignals(False)
+
+ @SafeSlot(bool)
+ def opening_shutter(self, enabled: bool):
+ logger.info(f"Shutter changed from GUI to: {enabled}")
+ self.shutter_toggle.blockSignals(True)
+ if enabled:
+ self.dev.omnyfsh.fshopen()
+ else:
+ self.dev.omnyfsh.fshclose()
+ # self.shutter_toggle.checked = enabled
+ self.shutter_toggle.blockSignals(False)
+
+
+ @SafeSlot(dict,dict)
+ def getting_shutter_status(self,data,meta):
+ shutter_open = bool(data.get("signals").get("omnyfsh_shutter").get("value"))
+ self.shutter_toggle.blockSignals(True)
+ self.shutter_toggle.checked = shutter_open
+ self.shutter_toggle.blockSignals(False)
+
+
@SafeSlot(bool, bool)
def on_motors_enable(self, x_enable: bool, y_enable: bool):
"""
@@ -327,58 +432,60 @@ class XRayEye(BECWidget, QWidget):
self.motor_control_2d.enable_controls_hor(x_enable)
self.motor_control_2d.enable_controls_ver(y_enable)
- @SafeSlot(int)
- def enable_submit_button(self, enable: int):
+ @SafeSlot(bool)
+ def enable_submit_button(self, enable: bool):
"""
Enable/disable submit button.
Args:
enable(int): -1 disable else enable
"""
- if enable == -1:
- self.submit_button.setEnabled(False)
- else:
+ if enable:
self.submit_button.setEnabled(True)
+ else:
+ self.submit_button.setEnabled(False)
+
+ @SafeSlot(dict,dict)
+ def on_dap_params(self,data,meta):
+ print('#######################################')
+ print('getting dap parameters')
+ print(f"data: {data}")
+ print(f"meta: {meta}")
+ self.waveform_x.auto_range(True)
+ self.waveform_y.auto_range(True)
+ # self.bec_dispatcher.disconnect_slot(self.device_updates, MessageEndpoints.device_readback("omny_xray_gui"))
+ curve_id = meta.get("curve_id")
+
+
+
+ if curve_id == "fit-x-SineModel":
+ self.dev.omny_xray_gui.fit_params_x.set(data).wait()
+ print(f"setting x data to {data}")
+ else:
+ self.dev.omny_xray_gui.fit_params_y.set(data).wait()
+ print(f"setting y data to {data}")
+ # self.bec_dispatcher.connect_slot(self.device_updates, MessageEndpoints.device_readback("omny_xray_gui"))
@SafeSlot(bool, bool)
def on_tomo_angle_readback(self, data: dict, meta: dict):
#TODO implement if needed
print(f"data: {data}")
print(f"meta: {meta}")
+
+ @SafeSlot()
+ def submit_fit_array(self,fit_array):
+ self.tab_widget.setCurrentIndex(1)
+ # self.fix_x.title = " got fit array"
+ print(f"got fit array {fit_array}")
+ self.waveform_x.curves[0].set_data(x=fit_array[0],y=fit_array[1])
+ # self.fit_x.set_data(x=fit_array[0],y=fit_array[1])
+ # self.fit_y.set_data(x=fit_array[0],y=fit_array[2])
- @SafeSlot(dict, dict)
- def device_updates(self, data: dict, meta: dict):
- """
- Slot to handle device updates from omny_xray_gui device.
-
- Args:
- data(dict): data from device
- meta(dict): metadata from device
- """
-
- signals = data.get('signals')
- enable_live_preview = signals.get("omny_xray_gui_update_frame_acq").get('value')
- enable_x_motor = signals.get("omny_xray_gui_enable_mv_x").get('value')
- enable_y_motor = signals.get("omny_xray_gui_enable_mv_y").get('value')
- self.on_live_view_enabled(bool(enable_live_preview))
- self.on_motors_enable(bool(enable_x_motor), bool(enable_y_motor))
-
- # Signals from epics gui device
- # send message
- user_message = signals.get("omny_xray_gui_send_message").get('value')
- self.user_message = user_message
- # sample name
- sample_message = signals.get("omny_xray_gui_sample_name").get('value')
- self.sample_name = sample_message
- # enable frame acquisition
- update_frame_acq = signals.get("omny_xray_gui_update_frame_acq").get('value')
- self.on_live_view_enabled(bool(update_frame_acq))
- # enable submit button
- enable_submit_button = signals.get("omny_xray_gui_submit").get('value')
- self.enable_submit_button(enable_submit_button)
@SafeSlot()
def submit(self):
"""Execute submit action by submit button."""
+ print('submit pushed')
+ self.submit_button.blockSignals(True)
if self.roi_manager.single_active_roi is None:
logger.warning("No active ROI")
return
@@ -400,12 +507,14 @@ class XRayEye(BECWidget, QWidget):
# submit roi coordinates
step = int(self.dev.omny_xray_gui.step.read().get("omny_xray_gui_step").get('value'))
- xval_x = getattr(self.dev.omny_xray_gui.xval_x, f"xval_x_{step}").set(roi_center_x)
- xval_y = getattr(self.dev.omny_xray_gui.yval_y, f"yval_y_{step}").set(roi_center_y)
- width_x = getattr(self.dev.omny_xray_gui.width_x, f"width_x_{step}").set(roi_width)
- width_y = getattr(self.dev.omny_xray_gui.width_y, f"width_y_{step}").set(roi_height)
+ xval_x = getattr(self.dev.omny_xray_gui, f"xval_x_{step}").set(roi_center_x)
+ xval_y = getattr(self.dev.omny_xray_gui, f"yval_y_{step}").set(roi_center_y)
+ width_x = getattr(self.dev.omny_xray_gui, f"width_x_{step}").set(roi_width)
+ width_y = getattr(self.dev.omny_xray_gui, f"width_y_{step}").set(roi_height)
self.dev.omny_xray_gui.submit.set(1)
-
+ print('submit done')
+ self.submit_button.blockSignals(False)
+
def cleanup(self):
"""Cleanup connections on widget close -> disconnect slots and stop live mode of camera."""
self.bec_dispatcher.disconnect_slot(self.device_updates, MessageEndpoints.device_readback("omny_xray_gui"))
@@ -416,9 +525,12 @@ if __name__ == "__main__":
import sys
from qtpy.QtWidgets import QApplication
+ from bec_widgets.utils import BECDispatcher
+ from bec_widgets.utils.colors import apply_theme
app = QApplication(sys.argv)
-
+ apply_theme("light")
+ dispatcher = BECDispatcher(gui_id='xray')
win = XRayEye()
win.resize(1000, 800)
diff --git a/csaxs_bec/device_configs/ptycho_flomni.yaml b/csaxs_bec/device_configs/ptycho_flomni.yaml
index e2a20c5..1c5d367 100644
--- a/csaxs_bec/device_configs/ptycho_flomni.yaml
+++ b/csaxs_bec/device_configs/ptycho_flomni.yaml
@@ -408,20 +408,20 @@ cam_xeye:
readOnly: false
readoutPriority: async
-cam_ids_rgb:
- description: Camera flOMNI Xray eye ID203
- deviceClass: csaxs_bec.devices.ids_cameras.ids_camera.IDSCamera
- deviceConfig:
- camera_id: 203
- bits_per_pixel: 24
- num_rotation_90: 3
- transpose: false
- force_monochrome: true
- m_n_colormode: 1
- enabled: true
- onFailure: buffer
- readOnly: false
- readoutPriority: async
+# cam_ids_rgb:
+# description: Camera flOMNI Xray eye ID203
+# deviceClass: csaxs_bec.devices.ids_cameras.ids_camera.IDSCamera
+# deviceConfig:
+# camera_id: 203
+# bits_per_pixel: 24
+# num_rotation_90: 2
+# transpose: false
+# force_monochrome: false
+# m_n_colormode: 1
+# enabled: true
+# onFailure: buffer
+# readOnly: false
+# readoutPriority: async
# ############################################################
@@ -450,19 +450,10 @@ omnyfsh:
#################### GUI Signals ###########################
############################################################
omny_xray_gui:
- description: Gui Epics signals
- deviceClass: csaxs_bec.devices.omny.xray_epics_gui.OMNYXRayEpicsGUI
+ description: Gui signals
+ deviceClass: csaxs_bec.devices.omny.xray_epics_gui.OMNYXRayAlignGUI
deviceConfig: {}
enabled: true
onFailure: buffer
readOnly: false
- readoutPriority: on_request
-
-calculated_signal:
- description: Calculated signal from alignment for fit
- deviceClass: ophyd_devices.ComputedSignal
- deviceConfig:
- compute_method: "def just_rand():\n return 42"
- enabled: true
- readOnly: false
- readoutPriority: baseline
\ No newline at end of file
+ readoutPriority: on_request
\ No newline at end of file
diff --git a/csaxs_bec/devices/epics/allied_vision_camera.py b/csaxs_bec/devices/epics/allied_vision_camera.py
new file mode 100644
index 0000000..870bca5
--- /dev/null
+++ b/csaxs_bec/devices/epics/allied_vision_camera.py
@@ -0,0 +1,149 @@
+"""Module for the EPICS integration of the AlliedVision Camera via Vimba SDK."""
+
+import threading
+import traceback
+from enum import IntEnum
+
+import numpy as np
+from bec_lib.logger import bec_logger
+from ophyd import Component as Cpt, Kind, Signal
+from ophyd.areadetector import ADComponent as ADCpt
+from ophyd.areadetector import DetectorBase
+from ophyd_devices import PreviewSignal
+from ophyd_devices.devices.areadetector.cam import VimbaDetectorCam
+from ophyd_devices.devices.areadetector.plugins import ImagePlugin_V35 as ImagePlugin
+from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase
+from typeguard import typechecked
+
+logger = bec_logger.logger
+
+
+class ACQUIRE_MODES(IntEnum):
+ """Acquiring enums for Allied Vision Camera"""
+
+ ACQUIRING = 1
+ DONE = 0
+
+
+class AlliedVisionCamera(PSIDeviceBase, DetectorBase):
+ """
+ Epics Area Detector interface for the Allied Vision Alvium G1-507m camera via Vimba SDK.
+ The IOC runs with under the prefix: 'X12SA-GIGECAM-AV1:'.
+ """
+
+ USER_ACCESS = ["start_live_mode", "stop_live_mode"]
+
+ cam = ADCpt(VimbaDetectorCam, "cam1:")
+ image = ADCpt(ImagePlugin, "image1:")
+
+ preview = Cpt(
+ PreviewSignal,
+ name="preview",
+ ndim=2,
+ num_rotation_90=0,
+ doc="Preview signal of the AlliedVision camera.",
+ )
+
+ live_mode_enabled = Cpt(
+ Signal,
+ name="live_mode_enabled",
+ value=False,
+ doc="Enable or disable live mode.",
+ kind=Kind.config,
+ )
+
+ def __init__(
+ self,
+ *,
+ name: str,
+ prefix: str,
+ poll_rate: int = 5,
+ scan_info=None,
+ device_manager=None,
+ **kwargs,
+ ):
+ super().__init__(
+ name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs
+ )
+ self._poll_thread = threading.Thread(
+ target=self._poll_array_data, daemon=True, name=f"{self.name}_poll_thread"
+ )
+ self._poll_thread_kill_event = threading.Event()
+ self._poll_start_event = threading.Event()
+ if poll_rate > 10:
+ logger.warning(f"Poll rate too high for Camera {self.name}, setting to 10 Hz max.")
+ poll_rate = 10
+ self._poll_rate = poll_rate
+ self._unique_array_id = 0
+ self._pv_timeout = 2.0
+ self.image: ImagePlugin
+ self._live_mode_lock = threading.RLock()
+ self.live_mode_enabled.subscribe(self._on_live_mode_enabled_changed, run=False)
+
+ def start_live_mode(self) -> None:
+ """Start live mode."""
+ self.live_mode_enabled.put(True)
+
+ def stop_live_mode(self) -> None:
+ """Stop live mode."""
+ self.live_mode_enabled.put(False)
+
+ def _on_live_mode_enabled_changed(self, *args, value, **kwargs) -> None:
+ self._apply_live_mode(bool(value))
+
+ def _apply_live_mode(self, enabled: bool) -> None:
+ with self._live_mode_lock:
+ if enabled:
+ if not self._poll_start_event.is_set():
+ self._poll_start_event.set()
+ self.cam.acquire.put(ACQUIRE_MODES.ACQUIRING.value) # Start acquisition
+ else:
+ logger.info(f"Live mode already started for {self.name}.")
+ return
+
+ if self._poll_start_event.is_set():
+ self._poll_start_event.clear()
+ self.cam.acquire.put(ACQUIRE_MODES.DONE.value) # Stop acquisition
+ else:
+ logger.info(f"Live mode already stopped for {self.name}.")
+
+ def on_connected(self):
+ """Reset the unique array ID on connection."""
+ self.cam.array_counter.set(0).wait(timeout=self._pv_timeout)
+ self.cam.array_callbacks.set(1).wait(timeout=self._pv_timeout)
+ self._poll_thread.start()
+
+ def _poll_array_data(self):
+ """Poll the array data for preview updates."""
+ while not self._poll_thread_kill_event.wait(1 / self._poll_rate):
+ while self._poll_start_event.wait():
+ try:
+ # First check if there is a new image
+ if self.image.unique_id.get() != self._unique_array_id:
+ self._unique_array_id = self.image.unique_id.get()
+ else:
+ continue # No new image, skip update
+ # Get new image data
+ value = self.image.array_data.get()
+ if value is None:
+ logger.info(f"No image data available for preview of {self.name}")
+ continue
+
+ array_size = self.image.array_size.get()
+ if array_size[0] == 0: # 2D image, not color image
+ array_size = array_size[1:]
+ # Geometry correction for the image
+ data = np.reshape(value, array_size)
+ self.preview.put(data)
+ except Exception: # pylint: disable=broad-except
+ content = traceback.format_exc()
+ logger.error(
+ f"Error while polling array data for preview of {self.name}: {content}"
+ )
+
+ def on_destroy(self):
+ """Stop the polling thread on destruction."""
+ self._poll_thread_kill_event.set()
+ self._poll_start_event.set()
+ if self._poll_thread.is_alive():
+ self._poll_thread.join(timeout=2)
diff --git a/csaxs_bec/devices/ids_cameras/base_integration/camera.py b/csaxs_bec/devices/ids_cameras/base_integration/camera.py
index fe521b5..38bdd89 100644
--- a/csaxs_bec/devices/ids_cameras/base_integration/camera.py
+++ b/csaxs_bec/devices/ids_cameras/base_integration/camera.py
@@ -156,7 +156,6 @@ class Camera:
camera_id (int): The ID of the camera device.
m_n_colormode (Literal[0, 1, 2, 3]): Color mode for the camera.
bits_per_pixel (Literal[8, 24]): Number of bits per pixel for the camera.
- live_mode (bool): Whether to enable live mode for the camera.
"""
def __init__(
diff --git a/csaxs_bec/devices/ids_cameras/ids_camera.py b/csaxs_bec/devices/ids_cameras/ids_camera.py
index 750e877..a5bf68e 100644
--- a/csaxs_bec/devices/ids_cameras/ids_camera.py
+++ b/csaxs_bec/devices/ids_cameras/ids_camera.py
@@ -3,21 +3,19 @@
from __future__ import annotations
import threading
-import time
-from typing import TYPE_CHECKING, Literal, Tuple, TypedDict
+from typing import TYPE_CHECKING, Literal
import numpy as np
+from ophyd import Component as Cpt, Signal, Kind
+
from bec_lib import messages
from bec_lib.logger import bec_logger
-from ophyd import Component as Cpt
+from csaxs_bec.devices.ids_cameras.base_integration.camera import Camera
from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase
from ophyd_devices.utils.bec_signals import AsyncSignal, PreviewSignal
-from csaxs_bec.devices.ids_cameras.base_integration.camera import Camera
-
if TYPE_CHECKING:
from bec_lib.devicemanager import ScanInfo
- from pydantic import ValidationInfo
logger = bec_logger.logger
@@ -45,8 +43,15 @@ class IDSCamera(PSIDeviceBase):
doc="Signal for the region of interest (ROI).",
async_update={"type": "add", "max_shape": [None]},
)
+ live_mode_enabled = Cpt(
+ Signal,
+ name="live_mode_enabled",
+ value=False,
+ doc="Enable or disable live mode.",
+ kind=Kind.config,
+ )
- USER_ACCESS = ["live_mode", "mask", "set_rect_roi", "get_last_image"]
+ USER_ACCESS = ["start_live_mode", "stop_live_mode", "mask", "set_rect_roi", "get_last_image"]
def __init__(
self,
@@ -83,15 +88,22 @@ class IDSCamera(PSIDeviceBase):
bits_per_pixel=bits_per_pixel,
connect=False,
)
- self._live_mode = False
self._inputs = {"live_mode": live_mode}
self._mask = np.zeros((1, 1), dtype=np.uint8)
self.image.num_rotation_90 = num_rotation_90
self.image.transpose = transpose
self._force_monochrome = force_monochrome
+ self.live_mode_enabled.subscribe(self._on_live_mode_enabled_changed, run=False)
+ self.live_mode_enabled.put(bool(live_mode))
############## Live Mode Methods ##############
+ def start_live_mode(self) -> None:
+ self.live_mode_enabled.put(True)
+
+ def stop_live_mode(self) -> None:
+ self.live_mode_enabled.put(False)
+
@property
def mask(self) -> np.ndarray:
"""Return the current region of interest (ROI) for the camera."""
@@ -114,22 +126,15 @@ class IDSCamera(PSIDeviceBase):
)
self._mask = value
- @property
- def live_mode(self) -> bool:
- """Return whether the camera is in live mode."""
- return self._live_mode
-
- @live_mode.setter
- def live_mode(self, value: bool):
- """Set the live mode for the camera."""
- if value != self._live_mode:
- if self.cam._connected is False: # $ pylint: disable=protected-access
- self.cam.on_connect()
- self._live_mode = value
- if value:
- self._start_live()
- else:
- self._stop_live()
+ def _on_live_mode_enabled_changed(self, *args, value, **kwargs):
+ """Callback for when live mode is changed."""
+ enabled = bool(value)
+ if enabled and self.cam._connected is False: # pylint: disable=protected-access
+ self.cam.on_connect()
+ if enabled:
+ self._start_live()
+ else:
+ self._stop_live()
def set_rect_roi(self, x: int, y: int, width: int, height: int):
"""Set the rectangular region of interest (ROI) for the camera."""
@@ -196,7 +201,7 @@ class IDSCamera(PSIDeviceBase):
"""Connect to the camera."""
self.cam.force_monochrome = self._force_monochrome
self.cam.on_connect()
- self.live_mode = self._inputs.get("live_mode", False)
+ self.live_mode_enabled.put(bool(self._inputs.get("live_mode", False)))
self.set_rect_roi(0, 0, self.cam.cam.width.value, self.cam.cam.height.value)
def on_destroy(self):
@@ -206,7 +211,7 @@ class IDSCamera(PSIDeviceBase):
def on_trigger(self):
"""Handle the trigger event."""
- if not self.live_mode:
+ if not bool(self.live_mode_enabled.get()):
return
image = self.image.get()
if image is not None:
diff --git a/csaxs_bec/devices/omny/webcam_viewer.py b/csaxs_bec/devices/omny/webcam_viewer.py
index a15b1fd..a150b27 100644
--- a/csaxs_bec/devices/omny/webcam_viewer.py
+++ b/csaxs_bec/devices/omny/webcam_viewer.py
@@ -2,7 +2,7 @@ import requests
import threading
import cv2
import numpy as np
-from ophyd import Device, Component as Cpt
+from ophyd import Device, Component as Cpt, Kind, Signal
from ophyd_devices import PreviewSignal
import traceback
@@ -13,6 +13,13 @@ logger = bec_logger.logger
class WebcamViewer(Device):
USER_ACCESS = ["start_live_mode", "stop_live_mode"]
preview = Cpt(PreviewSignal, ndim=2, num_rotation_90=0, transpose=False)
+ live_mode_enabled = Cpt(
+ Signal,
+ name="live_mode_enabled",
+ value=False,
+ doc="Enable or disable live mode.",
+ kind=Kind.config,
+ )
def __init__(self, url:str, name:str, num_rotation_90=0, transpose=False, **kwargs) -> None:
super().__init__(name=name, **kwargs)
@@ -21,20 +28,54 @@ class WebcamViewer(Device):
self._update_thread = None
self._buffer = b""
self._shutdown_event = threading.Event()
+ self._live_mode_lock = threading.RLock()
self.preview.num_rotation_90 = num_rotation_90
self.preview.transpose = transpose
+ self.live_mode_enabled.subscribe(self._on_live_mode_enabled_changed, run=False)
def start_live_mode(self) -> None:
- if self._connection is not None:
- return
- self._update_thread = threading.Thread(target=self._update_loop, daemon=True)
- self._update_thread.start()
+ self.live_mode_enabled.put(True)
+
+ def stop_live_mode(self) -> None:
+ self.live_mode_enabled.put(False)
+
+ def _on_live_mode_enabled_changed(self, *args, value, **kwargs) -> None:
+ self._apply_live_mode(bool(value))
+
+ def _apply_live_mode(self, enabled: bool) -> None:
+ with self._live_mode_lock:
+ if enabled:
+ if self._update_thread is not None and self._update_thread.is_alive():
+ return
+ self._shutdown_event.clear()
+ self._update_thread = threading.Thread(target=self._update_loop, daemon=True)
+ self._update_thread.start()
+ return
+
+ if self._update_thread is None:
+ return
+ self._shutdown_event.set()
+ if self._connection is not None:
+ try:
+ self._connection.close()
+ except Exception: # pylint: disable=broad-except
+ pass
+ self._connection = None
+ self._update_thread.join(timeout=2)
+ if self._update_thread.is_alive():
+ logger.warning("Webcam live mode thread did not stop within timeout.")
+ return
+ self._update_thread = None
+ self._buffer = b""
+ self._shutdown_event.clear()
def _update_loop(self) -> None:
while not self._shutdown_event.is_set():
try:
- self._connection = requests.get(self.url, stream=True)
+ self._connection = requests.get(self.url, stream=True, timeout=5)
for chunk in self._connection.iter_content(chunk_size=1024):
+ if self._shutdown_event.is_set():
+ break
self._buffer += chunk
start = self._buffer.find(b'\xff\xd8') # JPEG start
end = self._buffer.find(b'\xff\xd9') # JPEG end
@@ -50,16 +91,3 @@ class WebcamViewer(Device):
except Exception as exc:
content = traceback.format_exc()
logger.error(f"Image update loop failed: {content}")
-
- def stop_live_mode(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
diff --git a/csaxs_bec/devices/omny/xray_epics_gui.py b/csaxs_bec/devices/omny/xray_epics_gui.py
index 7db7bb7..5f783db 100644
--- a/csaxs_bec/devices/omny/xray_epics_gui.py
+++ b/csaxs_bec/devices/omny/xray_epics_gui.py
@@ -1,74 +1,46 @@
-
-from ophyd import Component as Cpt
+import numpy as np
+from ophyd import Component as Cpt, Signal, EpicsSignal
from ophyd import Device
from ophyd import DynamicDeviceComponent as Dcpt
-from ophyd import EpicsSignal
+class OMNYXRayAlignGUI(Device):
-
-class OMNYXRayEpicsGUI(Device):
-
- save_frame = Cpt(
- EpicsSignal, name="save_frame", read_pv="XOMNYI-XEYE-SAVFRAME:0",auto_monitor=True
- )
- update_frame_acqdone = Cpt(
- EpicsSignal, name="update_frame_acqdone", read_pv="XOMNYI-XEYE-ACQDONE:0",auto_monitor=True
- )
- update_frame_acq = Cpt(
- EpicsSignal, name="update_frame_acq", read_pv="XOMNYI-XEYE-ACQ:0",auto_monitor=True
- )
- width_y_dynamic = {
- f"width_y_{i}": (EpicsSignal, f"XOMNYI-XEYE-YWIDTH_Y:{i}", {"auto_monitor": True}) for i in range(0, 11)
- }
- width_y = Dcpt(width_y_dynamic)
- width_x_dynamic = {
- f"width_x_{i}": (EpicsSignal, f"XOMNYI-XEYE-XWIDTH_X:{i}", {"auto_monitor": True}) for i in range(0, 11)
- }
- width_x = Dcpt(width_x_dynamic)
- enable_mv_x = Cpt(
- EpicsSignal, name="enable_mv_x", read_pv="XOMNYI-XEYE-ENAMVX:0",auto_monitor=True
- )
- enable_mv_y = Cpt(
- EpicsSignal, name="enable_mv_y", read_pv="XOMNYI-XEYE-ENAMVY:0",auto_monitor=True
- )
- send_message = Cpt(
- EpicsSignal, name="send_message", read_pv="XOMNYI-XEYE-MESSAGE:0.DESC",auto_monitor=True
- )
- sample_name = Cpt(
- EpicsSignal, name="sample_name", read_pv="XOMNYI-XEYE-SAMPLENAME:0.DESC",auto_monitor=True
- )
- angle = Cpt(
- EpicsSignal, name="angle", read_pv="XOMNYI-XEYE-ANGLE:0",auto_monitor=True
- )
- pixel_size = Cpt(
- EpicsSignal, name="pixel_size", read_pv="XOMNYI-XEYE-PIXELSIZE:0",auto_monitor=True
- )
+ update_frame_acqdone = Cpt(Signal, value=0)
+ update_frame_acq = Cpt(Signal, value=0)
+ enable_mv_x = Cpt(Signal, value=0)
+ enable_mv_y = Cpt(Signal, value=0)
+ send_message = Cpt(Signal, value=0)
+ sample_name = Cpt(Signal, value=0)
+ angle = Cpt(Signal, value=0)
+ pixel_size = Cpt(Signal, value=0)
submit = Cpt(
EpicsSignal, name="submit", read_pv="XOMNYI-XEYE-SUBMIT:0",auto_monitor=True
- )
- step = Cpt(
- EpicsSignal, name="step", read_pv="XOMNYI-XEYE-STEP:0",auto_monitor=True
- )
- xval_x_dynamic = {
- f"xval_x_{i}": (EpicsSignal, f"XOMNYI-XEYE-XVAL_X:{i}", {"auto_monitor": True}) for i in range(0, 11)
- }
- xval_x = Dcpt(xval_x_dynamic)
- yval_y_dynamic = {
- f"yval_y_{i}": (EpicsSignal, f"XOMNYI-XEYE-YVAL_Y:{i}", {"auto_monitor": True}) for i in range(0, 11)
- }
- yval_y = Dcpt(yval_y_dynamic)
- recbg = Cpt(
- EpicsSignal, name="recbg", read_pv="XOMNYI-XEYE-RECBG:0",auto_monitor=True
- )
- stage_pos_x_dynamic = {
- f"stage_pos_x_{i}": (EpicsSignal, f"XOMNYI-XEYE-STAGEPOSX:{i}", {"auto_monitor": True}) for i in range(1, 6)
- }
- stage_pos_x = Dcpt(stage_pos_x_dynamic)
- mvx = Cpt(
- EpicsSignal, name="mvx", read_pv="XOMNYI-XEYE-MVX:0",auto_monitor=True
- )
- mvy = Cpt(
- EpicsSignal, name="mvy", read_pv="XOMNYI-XEYE-MVY:0",auto_monitor=True
- )
+ )
+ step = Cpt(Signal, value=0)
+ recbg = Cpt(Signal, value=0)
+ mvx = Cpt(Signal, value=0)
+ mvy = Cpt(Signal, value=0)
+ fit_array = Cpt(Signal, value=np.zeros((3, 10)))
+ fit_params_x = Cpt(Signal, value=np.zeros((2, 3)))
+ fit_params_y = Cpt(Signal, value=np.zeros((2, 3)))
+ # Generate width_y_0 to width_y_10
+ for i in range(11):
+ locals()[f'width_y_{i}'] = Cpt(Signal, value=0)
+
+ # Generate width_x_0 to width_x_10
+ for i in range(11):
+ locals()[f'width_x_{i}'] = Cpt(Signal, value=0)
+
+ # Generate xval_x_0 to xval_x_10
+ for i in range(11):
+ locals()[f'xval_x_{i}'] = Cpt(Signal, value=0)
+
+ # Generate yval_y_0 to yval_y_10
+ for i in range(11):
+ locals()[f'yval_y_{i}'] = Cpt(Signal, value=0)
+
+ # Generate stage_pos_x_1 to stage_pos_x_5
+ for i in range(1, 6):
+ locals()[f'stage_pos_x_{i}'] = Cpt(Signal, value=0)