Compare commits

...

6 Commits

Author SHA1 Message Date
6873ef8287 test: fix lamni test
All checks were successful
CI for csaxs_bec / test (pull_request) Successful in 1m56s
CI for csaxs_bec / test (push) Successful in 1m58s
2026-03-06 15:11:28 +01:00
x01dc
70fa96bd58 added beck startup
Some checks failed
CI for csaxs_bec / test (pull_request) Failing after 1m53s
CI for csaxs_bec / test (push) Failing after 1m55s
2026-03-06 14:39:34 +01:00
x01dc
5155ba9b77 now reading encoder values for axes with encoder 2026-03-06 14:39:34 +01:00
488156fd87 documentation file for the 30 nm FZPs
All checks were successful
CI for csaxs_bec / test (push) Successful in 2m0s
CI for csaxs_bec / test (pull_request) Successful in 1m56s
2026-03-06 13:11:35 +01:00
4721ec404b fix(mcs): remove info logs
All checks were successful
CI for csaxs_bec / test (pull_request) Successful in 1m54s
CI for csaxs_bec / test (push) Successful in 1m58s
2026-03-04 09:13:55 +01:00
4d69f8f90f fix(bec_widgets): removed omny alignment old gui
All checks were successful
CI for csaxs_bec / test (push) Successful in 1m55s
2026-03-02 21:00:14 +01:00
16 changed files with 148 additions and 463 deletions

View File

@@ -13,69 +13,10 @@ logger = bec_logger.logger
_Widgets = { _Widgets = {
"OmnyAlignment": "OmnyAlignment",
"XRayEye": "XRayEye", "XRayEye": "XRayEye",
} }
class OmnyAlignment(RPCBase):
@property
@rpc_call
def enable_live_view(self):
"""
None
"""
@enable_live_view.setter
@rpc_call
def enable_live_view(self):
"""
None
"""
@property
@rpc_call
def user_message(self):
"""
None
"""
@user_message.setter
@rpc_call
def user_message(self):
"""
None
"""
@property
@rpc_call
def sample_name(self):
"""
None
"""
@sample_name.setter
@rpc_call
def sample_name(self):
"""
None
"""
@property
@rpc_call
def enable_move_buttons(self):
"""
None
"""
@enable_move_buttons.setter
@rpc_call
def enable_move_buttons(self):
"""
None
"""
class XRayEye(RPCBase): class XRayEye(RPCBase):
@rpc_call @rpc_call
def active_roi(self) -> "BaseROI | None": def active_roi(self) -> "BaseROI | None":
@@ -83,20 +24,6 @@ class XRayEye(RPCBase):
Return the currently active ROI, or None if no ROI is active. 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 @property
@rpc_call @rpc_call
def user_message(self): def user_message(self):

View File

@@ -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_())

View File

@@ -1 +0,0 @@
{'files': ['omny_alignment.py']}

View File

@@ -1,125 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>988</width>
<height>821</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="2">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2">
<widget class="QPushButton" name="moveRightButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="moveLeftButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="moveUpButton">
<property name="text">
<string>Up</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="moveDownButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="confirmButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="1">
<widget class="QLineEdit" name="sampleLineEdit"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="messageLineEdit"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Sample</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Message</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="3">
<widget class="Image" name="image">
<property name="enable_toolbar" stdset="0">
<bool>false</bool>
</property>
<property name="inner_axes" stdset="0">
<bool>false</bool>
</property>
<property name="monitor" stdset="0">
<string>cam_xeye</string>
</property>
<property name="rotation" stdset="0">
<number>3</number>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="ToggleSwitch" name="liveViewSwitch"/>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Live View</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Image</class>
<extends>QWidget</extends>
<header>image</header>
</customwidget>
<customwidget>
<class>ToggleSwitch</class>
<extends>QWidget</extends>
<header>toggle_switch</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -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 = """
<ui language='c++'>
<widget class='OmnyAlignment' name='omny_alignment'>
</widget>
</ui>
"""
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()

View File

@@ -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()

View File

@@ -33,46 +33,48 @@ class XRayEye2DControl(BECWidget, QWidget):
self.get_bec_shortcuts() self.get_bec_shortcuts()
self._step_size = step_size self._step_size = step_size
self.root_layout = QGridLayout(self) self.root_layout = QGridLayout(self)
self.setStyleSheet(""" self.setStyleSheet(
"""
QToolButton { QToolButton {
border: 1px solid; border: 1px solid;
border-radius: 4px; border-radius: 4px;
} }
""") """
)
# Up # Up
self.move_up_button = QToolButton(parent=self) self.move_up_button = QToolButton(parent=self)
self.move_up_button.setIcon(material_icon('keyboard_double_arrow_up')) self.move_up_button.setIcon(material_icon("keyboard_double_arrow_up"))
self.root_layout.addWidget(self.move_up_button, 0, 2) self.root_layout.addWidget(self.move_up_button, 0, 2)
# Up tweak button # Up tweak button
self.move_up_tweak_button = QToolButton(parent=self) self.move_up_tweak_button = QToolButton(parent=self)
self.move_up_tweak_button.setIcon(material_icon('keyboard_arrow_up')) self.move_up_tweak_button.setIcon(material_icon("keyboard_arrow_up"))
self.root_layout.addWidget(self.move_up_tweak_button, 1, 2) self.root_layout.addWidget(self.move_up_tweak_button, 1, 2)
# Left # Left
self.move_left_button = QToolButton(parent=self) self.move_left_button = QToolButton(parent=self)
self.move_left_button.setIcon(material_icon('keyboard_double_arrow_left')) self.move_left_button.setIcon(material_icon("keyboard_double_arrow_left"))
self.root_layout.addWidget(self.move_left_button, 2, 0) self.root_layout.addWidget(self.move_left_button, 2, 0)
# Left tweak button # Left tweak button
self.move_left_tweak_button = QToolButton(parent=self) self.move_left_tweak_button = QToolButton(parent=self)
self.move_left_tweak_button.setIcon(material_icon('keyboard_arrow_left')) self.move_left_tweak_button.setIcon(material_icon("keyboard_arrow_left"))
self.root_layout.addWidget(self.move_left_tweak_button, 2, 1) self.root_layout.addWidget(self.move_left_tweak_button, 2, 1)
# Right # Right
self.move_right_button = QToolButton(parent=self) self.move_right_button = QToolButton(parent=self)
self.move_right_button.setIcon(material_icon('keyboard_double_arrow_right')) self.move_right_button.setIcon(material_icon("keyboard_double_arrow_right"))
self.root_layout.addWidget(self.move_right_button, 2, 4) self.root_layout.addWidget(self.move_right_button, 2, 4)
# Right tweak button # Right tweak button
self.move_right_tweak_button = QToolButton(parent=self) self.move_right_tweak_button = QToolButton(parent=self)
self.move_right_tweak_button.setIcon(material_icon('keyboard_arrow_right')) self.move_right_tweak_button.setIcon(material_icon("keyboard_arrow_right"))
self.root_layout.addWidget(self.move_right_tweak_button, 2, 3) self.root_layout.addWidget(self.move_right_tweak_button, 2, 3)
# Down # Down
self.move_down_button = QToolButton(parent=self) self.move_down_button = QToolButton(parent=self)
self.move_down_button.setIcon(material_icon('keyboard_double_arrow_down')) self.move_down_button.setIcon(material_icon("keyboard_double_arrow_down"))
self.root_layout.addWidget(self.move_down_button, 4, 2) self.root_layout.addWidget(self.move_down_button, 4, 2)
# Down tweak button # Down tweak button
self.move_down_tweak_button = QToolButton(parent=self) self.move_down_tweak_button = QToolButton(parent=self)
self.move_down_tweak_button.setIcon(material_icon('keyboard_arrow_down')) self.move_down_tweak_button.setIcon(material_icon("keyboard_arrow_down"))
self.root_layout.addWidget(self.move_down_tweak_button, 3, 2) self.root_layout.addWidget(self.move_down_tweak_button, 3, 2)
# Connections # Connections
@@ -124,8 +126,15 @@ class XRayEye2DControl(BECWidget, QWidget):
class XRayEye(BECWidget, QWidget): class XRayEye(BECWidget, QWidget):
USER_ACCESS = ["active_roi", "enable_live_view", "enable_live_view.setter", "user_message", "user_message.setter", USER_ACCESS = [
"sample_name", "sample_name.setter", "enable_move_buttons", "enable_move_buttons.setter"] "active_roi",
"user_message",
"user_message.setter",
"sample_name",
"sample_name.setter",
"enable_move_buttons",
"enable_move_buttons.setter",
]
PLUGIN = True PLUGIN = True
def __init__(self, parent=None, **kwargs): def __init__(self, parent=None, **kwargs):
@@ -136,7 +145,9 @@ class XRayEye(BECWidget, QWidget):
self._make_connections() self._make_connections()
# Connection to redis endpoints # Connection to redis endpoints
self.bec_dispatcher.connect_slot(self.device_updates, MessageEndpoints.device_readback("omny_xray_gui")) self.bec_dispatcher.connect_slot(
self.device_updates, MessageEndpoints.device_readback("omny_xray_gui")
)
self.connect_motors() self.connect_motors()
self.resize(800, 600) self.resize(800, 600)
QTimer.singleShot(0, self._init_gui_trigger) QTimer.singleShot(0, self._init_gui_trigger)
@@ -145,7 +156,9 @@ class XRayEye(BECWidget, QWidget):
self.core_layout = QHBoxLayout(self) self.core_layout = QHBoxLayout(self)
self.image = Image(parent=self) self.image = Image(parent=self)
self.image.enable_toolbar = False # Disable default toolbar to not allow to user set anything 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.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.plot_item.vb.invertY(True) # #TODO Invert y axis to match logic of LabView GUI
@@ -156,8 +169,9 @@ class XRayEye(BECWidget, QWidget):
self.control_panel_layout.setSpacing(10) self.control_panel_layout.setSpacing(10)
# ROI toolbar + Live toggle (header row) # ROI toolbar + Live toggle (header row)
self.roi_manager = ROIPropertyTree(parent=self, image_widget=self.image, compact=True, self.roi_manager = ROIPropertyTree(
compact_orientation="horizontal") parent=self, image_widget=self.image, compact=True, compact_orientation="horizontal"
)
header_row = QHBoxLayout() header_row = QHBoxLayout()
header_row.setContentsMargins(0, 0, 0, 0) header_row.setContentsMargins(0, 0, 0, 0)
header_row.setSpacing(8) header_row.setSpacing(8)
@@ -230,7 +244,9 @@ class XRayEye(BECWidget, QWidget):
# Make connections # Make connections
self.live_preview_toggle.enabled.connect(self.on_live_view_enabled) self.live_preview_toggle.enabled.connect(self.on_live_view_enabled)
self.step_size.valueChanged.connect(lambda x: self.motor_control_2d.setProperty("step_size", x)) self.step_size.valueChanged.connect(
lambda x: self.motor_control_2d.setProperty("step_size", x)
)
self.submit_button.clicked.connect(self.submit) self.submit_button.clicked.connect(self.submit)
def _create_separator(self): def _create_separator(self):
@@ -248,12 +264,14 @@ class XRayEye(BECWidget, QWidget):
################################################################################ ################################################################################
def connect_motors(self): def connect_motors(self):
""" Checks one of the possible motors for flomni, omny and lamni setup.""" """Checks one of the possible motors for flomni, omny and lamni setup."""
possible_motors = ['osamroy', 'lsamrot', 'fsamroy'] possible_motors = ["osamroy", "lsamrot", "fsamroy"]
for motor in possible_motors: for motor in possible_motors:
if motor in self.dev: if motor in self.dev:
self.bec_dispatcher.connect_slot(self.on_tomo_angle_readback, MessageEndpoints.device_readback(motor)) self.bec_dispatcher.connect_slot(
self.on_tomo_angle_readback, MessageEndpoints.device_readback(motor)
)
logger.info(f"Succesfully connected to {motor}") logger.info(f"Succesfully connected to {motor}")
################################################################################ ################################################################################
@@ -341,7 +359,7 @@ class XRayEye(BECWidget, QWidget):
@SafeSlot(bool, bool) @SafeSlot(bool, bool)
def on_tomo_angle_readback(self, data: dict, meta: dict): def on_tomo_angle_readback(self, data: dict, meta: dict):
#TODO implement if needed # TODO implement if needed
print(f"data: {data}") print(f"data: {data}")
print(f"meta: {meta}") print(f"meta: {meta}")
@@ -355,25 +373,25 @@ class XRayEye(BECWidget, QWidget):
meta(dict): metadata from device meta(dict): metadata from device
""" """
signals = data.get('signals') signals = data.get("signals")
enable_live_preview = signals.get("omny_xray_gui_update_frame_acq").get('value') 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_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') enable_y_motor = signals.get("omny_xray_gui_enable_mv_y").get("value")
self.on_live_view_enabled(bool(enable_live_preview)) self.on_live_view_enabled(bool(enable_live_preview))
self.on_motors_enable(bool(enable_x_motor), bool(enable_y_motor)) self.on_motors_enable(bool(enable_x_motor), bool(enable_y_motor))
# Signals from epics gui device # Signals from epics gui device
# send message # send message
user_message = signals.get("omny_xray_gui_send_message").get('value') user_message = signals.get("omny_xray_gui_send_message").get("value")
self.user_message = user_message self.user_message = user_message
# sample name # sample name
sample_message = signals.get("omny_xray_gui_sample_name").get('value') sample_message = signals.get("omny_xray_gui_sample_name").get("value")
self.sample_name = sample_message self.sample_name = sample_message
# enable frame acquisition # enable frame acquisition
update_frame_acq = signals.get("omny_xray_gui_update_frame_acq").get('value') update_frame_acq = signals.get("omny_xray_gui_update_frame_acq").get("value")
self.on_live_view_enabled(bool(update_frame_acq)) self.on_live_view_enabled(bool(update_frame_acq))
# enable submit button # enable submit button
enable_submit_button = signals.get("omny_xray_gui_submit").get('value') enable_submit_button = signals.get("omny_xray_gui_submit").get("value")
self.enable_submit_button(enable_submit_button) self.enable_submit_button(enable_submit_button)
@SafeSlot() @SafeSlot()
@@ -383,22 +401,24 @@ class XRayEye(BECWidget, QWidget):
logger.warning("No active ROI") logger.warning("No active ROI")
return return
roi_coordinates = self.roi_manager.single_active_roi.get_coordinates() roi_coordinates = self.roi_manager.single_active_roi.get_coordinates()
roi_center_x = roi_coordinates['center_x'] roi_center_x = roi_coordinates["center_x"]
roi_center_y = roi_coordinates['center_y'] roi_center_y = roi_coordinates["center_y"]
# Case of rectangular ROI # Case of rectangular ROI
if isinstance(self.roi_manager.single_active_roi, RectangularROI): if isinstance(self.roi_manager.single_active_roi, RectangularROI):
roi_width = roi_coordinates['width'] roi_width = roi_coordinates["width"]
roi_height = roi_coordinates['height'] roi_height = roi_coordinates["height"]
elif isinstance(self.roi_manager.single_active_roi, CircularROI): elif isinstance(self.roi_manager.single_active_roi, CircularROI):
roi_width = roi_coordinates['diameter'] roi_width = roi_coordinates["diameter"]
roi_height = roi_coordinates['radius'] roi_height = roi_coordinates["radius"]
else: else:
logger.warning("Unsupported ROI type for submit action.") logger.warning("Unsupported ROI type for submit action.")
return return
print(f"current roi: x:{roi_center_x}, y:{roi_center_y}, w:{roi_width},h:{roi_height}") #TODO remove when will be not needed for debugging print(
f"current roi: x:{roi_center_x}, y:{roi_center_y}, w:{roi_width},h:{roi_height}"
) # TODO remove when will be not needed for debugging
# submit roi coordinates # submit roi coordinates
step = int(self.dev.omny_xray_gui.step.read().get("omny_xray_gui_step").get('value')) 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_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) xval_y = getattr(self.dev.omny_xray_gui.yval_y, f"yval_y_{step}").set(roi_center_y)
@@ -408,10 +428,13 @@ class XRayEye(BECWidget, QWidget):
def cleanup(self): def cleanup(self):
"""Cleanup connections on widget close -> disconnect slots and stop live mode of camera.""" """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")) self.bec_dispatcher.disconnect_slot(
getattr(self.dev,CAMERA[0]).live_mode = False self.device_updates, MessageEndpoints.device_readback("omny_xray_gui")
)
getattr(self.dev, CAMERA[0]).live_mode = False
super().cleanup() super().cleanup()
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys

View File

@@ -271,4 +271,20 @@ rty:
enabled: true enabled: true
readOnly: False readOnly: False
############################################################
######################### Cameras ##########################
############################################################
cam_xeye:
description: Camera LamNI Xray eye ID15
deviceClass: csaxs_bec.devices.ids_cameras.ids_camera.IDSCamera
deviceConfig:
camera_id: 15
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

View File

@@ -443,7 +443,6 @@ class MCSCardCSAXS(PSIDeviceBase, MCSCard):
while not self._scan_done_thread_kill_event.is_set(): while not self._scan_done_thread_kill_event.is_set():
while self._start_monitor_async_data_emission.wait(): while self._start_monitor_async_data_emission.wait():
try: try:
logger.debug(f"Monitoring async data emission for {self.name}...")
if ( if (
hasattr(self.scan_info.msg, "num_points") hasattr(self.scan_info.msg, "num_points")
and self.scan_info.msg.num_points is not None and self.scan_info.msg.num_points is not None
@@ -453,7 +452,6 @@ class MCSCardCSAXS(PSIDeviceBase, MCSCard):
for callback in self._scan_done_callbacks: for callback in self._scan_done_callbacks:
callback(exception=None) callback(exception=None)
else: else:
logger.info(f"Current data index is {self._current_data_index}")
if self._current_data_index >= 1: if self._current_data_index >= 1:
for callback in self._scan_done_callbacks: for callback in self._scan_done_callbacks:
callback(exception=None) callback(exception=None)

View File

@@ -15,7 +15,6 @@ from csaxs_bec.devices.omny.galil.galil_ophyd import (
GalilAxesReferenced, GalilAxesReferenced,
GalilController, GalilController,
GalilMotorIsMoving, GalilMotorIsMoving,
GalilMotorResolution,
GalilSetpointSignal, GalilSetpointSignal,
GalilSignalRO, GalilSignalRO,
retry_once, retry_once,
@@ -24,6 +23,19 @@ from csaxs_bec.devices.omny.galil.galil_ophyd import (
logger = bec_logger.logger logger = bec_logger.logger
class GalilMotorResolution(GalilSignalRO):
@retry_once
@threadlocked
def _socket_get(self):
if self.parent.axis_Id_numeric < 6:
return float(
self.controller.socket_put_and_receive(f"MG encpermm[{self.parent.axis_Id_numeric}]")
)
else:
return float(
self.controller.socket_put_and_receive(f"MG stppermm[{self.parent.axis_Id_numeric}]")
)
class LamniGalilController(GalilController): class LamniGalilController(GalilController):
@@ -154,10 +166,17 @@ class LamniGalilReadbackSignal(GalilSignalRO):
Returns: Returns:
float: Readback value after adjusting for sign and motor resolution. float: Readback value after adjusting for sign and motor resolution.
""" """
current_pos = float(self.controller.socket_put_and_receive(f"TD{self.parent.axis_Id}")) if self.parent.axis_Id_numeric < 6:
current_pos *= self.parent.sign current_pos = float(self.controller.socket_put_and_receive(f"TP{self.parent.axis_Id}"))
step_mm = self.parent.motor_resolution.get() current_pos *= self.parent.sign
return current_pos / step_mm encoder_resolution = self.parent.motor_resolution.get()
logger.info(f"Read galil encoder position of axis {self.parent.axis_Id_numeric} to be TP {current_pos} with resolution {encoder_resolution}")
return current_pos / encoder_resolution
else:
current_pos = float(self.controller.socket_put_and_receive(f"TD{self.parent.axis_Id}"))
current_pos *= self.parent.sign
step_mm = self.parent.motor_resolution.get()
return current_pos / step_mm
def read(self): def read(self):
self._metadata["timestamp"] = time.time() self._metadata["timestamp"] = time.time()

View File

@@ -170,6 +170,9 @@ class LamNIMixin:
self.device_manager.devices.lsamx.read_only = True self.device_manager.devices.lsamx.read_only = True
self.device_manager.devices.lsamy.read_only = True self.device_manager.devices.lsamy.read_only = True
#update angle readback before start of the scan
yield from self.stubs.send_rpc_and_wait("lsamrot", "readback.get")
yield from self.stubs.send_rpc_and_wait("rtx", "controller.feedback_enable_without_reset") yield from self.stubs.send_rpc_and_wait("rtx", "controller.feedback_enable_without_reset")

View File

@@ -108,12 +108,17 @@ The nano-positioning is controlled by a feedback loop running on a real-time lin
Once the loop has started, it is possible to start bec with the flOMNI configuration file. Once the loop has started, it is possible to start bec with the flOMNI configuration file.
Starting bec with session will load the scripts
`bec --session flomni`
The flOMNI scripts can be loaded manually by
`from csaxs_bec.bec_ipython_client.plugins.flomni import Flomni`
`flomni = Flomni(bec)`
Loading the flOMNI configuration (this command will load the OMNY configuration only - isolated from the beamline) Loading the flOMNI configuration (this command will load the OMNY configuration only - isolated from the beamline)
`bec.config.update_session_with_file("/bec/csaxs_bec/csaxs_bec/device_configs/flomni_config.yaml")` `bec.config.update_session_with_file("/bec/csaxs_bec/csaxs_bec/device_configs/flomni_config.yaml")`
Loading the flOMNI scripts
`from csaxs_bec.bec_ipython_client.plugins.flomni import Flomni`
`flomni = Flomni(bec)`
If the realtime system is restarted, bec will lose communication. To restart: If the realtime system is restarted, bec will lose communication. To restart:
`flomni.rt_off()` … then wait a few seconds `flomni.rt_off()` … then wait a few seconds
@@ -138,10 +143,14 @@ This script will first verify that the stages are not in an initialized state, a
The positions of the optics stages are stored as stage parameters and are thus linked to the configuration file. The positions of the optics stages are stored as stage parameters and are thus linked to the configuration file.
Example: The OSAx “in” position can be reviewed by `dev.fosax.user_parameter` Example: The OSAx “in” position can be reviewed by `dev.fosax.user_parameter`
Update the value by (example "fosax", "in") by `dev.fosax.update_user_parameter({"in":value})` Update the value by (example "fosax", "in") by `dev.fosax.update_user_parameter({"in":value})`
Important note: if these values are changed, they are not automatically stored to the config file and will only be available in the current session.
`flomni.ffzp_info()` shows info about the available FZPs at the current energy of the beamline. Optional parameter is the photon _energy_ in keV. `flomni.ffzp_info()` shows info about the available FZPs at the current energy of the beamline. Optional parameter is the photon _energy_ in keV.
Example: `flomni.ffzp_info(6.2)` Example: `flomni.ffzp_info(6.2)`
Documents about availabe optics can be accessed by
`flomni.flomnigui_docs`
The [laser feedback](user.ptychography.flomni.laser_feedback) will be disabled and fine alignment lost if foptx/y are moved! The [laser feedback](user.ptychography.flomni.laser_feedback) will be disabled and fine alignment lost if foptx/y are moved!
Following functions exist to move the optics in and out, with self-explaining naming. Following functions exist to move the optics in and out, with self-explaining naming.

View File

@@ -102,13 +102,16 @@ The nano-positioning is controlled by a feedback loop running on a real-time lin
Once the loop has started, it is possible to start bec with the LamNI configuration file. Once the loop has started, it is possible to start bec with the LamNI configuration file.
Loading the LamNI configuration (this command will load the LamNI configuration only - isolated from the beamline) Loading the LamNI scripts is done by starting bec as
`bec.config.update_session_with_file("/bec/csaxs_bec/csaxs_bec/device_configs/lamni_config.yaml")` `bec --session lamni`
Loading the LamNI scripts The scripts can alternatively manually be loaded by
`from csaxs_bec.bec_ipython_client.plugins.LamNI import LamNI` `from csaxs_bec.bec_ipython_client.plugins.LamNI import LamNI`
`lamni = LamNI(bec)` `lamni = LamNI(bec)`
Loading the LamNI configuration (this command will load the LamNI configuration only - isolated from the beamline)
`bec.config.update_session_with_file("/bec/csaxs_bec/csaxs_bec/device_configs/lamni_config.yaml")`
If the realtime system is restarted, BEC will lose communication. To restart: If the realtime system is restarted, BEC will lose communication. To restart:
`lamni.rt_off()` … then wait a 10 seconds `lamni.rt_off()` … then wait a 10 seconds
`lamni.rt_on()` `lamni.rt_on()`
@@ -152,6 +155,12 @@ The underlying scan function can be called as
Use `scans.lamni_fermat_scan?`for detailed information. A prerequisite for scanning is a running feedback system. Use `scans.lamni_fermat_scan?`for detailed information. A prerequisite for scanning is a running feedback system.
### GUI tools
During operation the BEC GUI will show the relevant cameras or progress information. To manually switch view TAB completion on 'lamni.lamnigui_' will show all options to control the GUI. Most useful
'lamni.lamnigui_show_progress()' will show the measurement progress GUI
'lamnigui_show_xeyealign()' will show the XrayEye alignment GUI
### X-ray optics alignment ### X-ray optics alignment
The positions of the optics stages are stored as stage parameters and are thus linked to the configuration file. The positions of the optics stages are stored as stage parameters and are thus linked to the configuration file.

View File

@@ -229,6 +229,22 @@ def device_manager_mock():
"kwargs": {}, "kwargs": {},
}, },
), ),
messages.DeviceInstructionMessage(
metadata={
"readout_priority": "monitored",
"RID": "1234",
"device_instr_id": "diid",
},
device="lsamrot",
action="rpc",
parameter={
"device": "lsamrot",
"func": "readback.get",
"rpc_id": "rpc_id",
"args": (),
"kwargs": {},
},
),
messages.DeviceInstructionMessage( messages.DeviceInstructionMessage(
metadata={ metadata={
"readout_priority": "monitored", "readout_priority": "monitored",