mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-14 03:31:50 +02:00
feat: motor selection
This commit is contained in:
@ -6,8 +6,8 @@
|
|||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1115</width>
|
<width>1120</width>
|
||||||
<height>506</height>
|
<height>547</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
@ -43,6 +43,12 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QComboBox" name="comboBox_motor_x"/>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QComboBox" name="comboBox_motor_y"/>
|
||||||
|
</item>
|
||||||
<item row="1" column="0">
|
<item row="1" column="0">
|
||||||
<widget class="QLabel" name="label_5">
|
<widget class="QLabel" name="label_5">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
@ -50,11 +56,12 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="3" column="0" colspan="2">
|
||||||
<widget class="QComboBox" name="comboBox"/>
|
<widget class="QPushButton" name="pushButton_connecMotors">
|
||||||
</item>
|
<property name="text">
|
||||||
<item row="2" column="1">
|
<string>Connect Motors</string>
|
||||||
<widget class="QComboBox" name="comboBox_2"/>
|
</property>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
@ -208,7 +215,7 @@
|
|||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QGroupBox" name="groupBox">
|
<widget class="QGroupBox" name="motorControl_absolute">
|
||||||
<property name="title">
|
<property name="title">
|
||||||
<string>Move Absolute</string>
|
<string>Move Absolute</string>
|
||||||
</property>
|
</property>
|
||||||
@ -271,7 +278,7 @@
|
|||||||
<property name="currentIndex">
|
<property name="currentIndex">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QWidget" name="tab">
|
<widget class="QWidget" name="tab_coordinates">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Coordinates</string>
|
<string>Coordinates</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
@ -307,7 +314,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_3">
|
<widget class="QWidget" name="tab_settings">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Settings</string>
|
<string>Settings</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
@ -510,7 +517,7 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<widget class="QWidget" name="tab_2">
|
<widget class="QWidget" name="tab_queue">
|
||||||
<attribute name="title">
|
<attribute name="title">
|
||||||
<string>Queue</string>
|
<string>Queue</string>
|
||||||
</attribute>
|
</attribute>
|
||||||
|
@ -31,6 +31,10 @@ class MotorApp(QWidget):
|
|||||||
current_path = os.path.dirname(__file__)
|
current_path = os.path.dirname(__file__)
|
||||||
uic.loadUi(os.path.join(current_path, "motor_controller.ui"), self)
|
uic.loadUi(os.path.join(current_path, "motor_controller.ui"), self)
|
||||||
|
|
||||||
|
# Motor Control Thread
|
||||||
|
self.motor_thread = MotorControl()
|
||||||
|
|
||||||
|
self.motor_x, self.motor_y = None, None # dev.samx, dev.samy
|
||||||
self.limit_x, self.limit_y = None, None
|
self.limit_x, self.limit_y = None, None
|
||||||
|
|
||||||
# Coordinates tracking
|
# Coordinates tracking
|
||||||
@ -39,21 +43,44 @@ class MotorApp(QWidget):
|
|||||||
self.num_dim_points = 15 # Number of points to dim gradually
|
self.num_dim_points = 15 # Number of points to dim gradually
|
||||||
|
|
||||||
# QThread for motor movement + signals
|
# QThread for motor movement + signals
|
||||||
self.motor_thread = MotorControl()
|
self.motor_thread.motors_loaded.connect(self.get_available_motors)
|
||||||
|
self.motor_thread.motors_selected.connect(self.get_selected_motors)
|
||||||
self.motor_thread.limits_retrieved.connect(self.update_limits)
|
self.motor_thread.limits_retrieved.connect(self.update_limits)
|
||||||
|
|
||||||
# UI
|
# UI
|
||||||
self.init_ui()
|
self.init_ui()
|
||||||
self.tag_N = 1 # position label for saved coordinates
|
self.tag_N = 1 # position label for saved coordinates
|
||||||
|
|
||||||
# Initialize current coordinates with the provided initial coordinates
|
# Get all motors available
|
||||||
self.motor_thread.retrieve_motor_limits(dev.samx, dev.samy)
|
self.motor_thread.retrieve_all_motors()
|
||||||
|
|
||||||
print(f"Init limits: samx:{self.limit_x}, samy:{self.limit_y}")
|
# Initialize current coordinates with the provided initial coordinates
|
||||||
|
# self.motor_thread.retrieve_motor_limits(self.motor_x, self.motor_y) #TODO move after connection of motors
|
||||||
|
|
||||||
|
# print(f"Init limits: samx:{self.limit_x}, samy:{self.limit_y}") # TODO get to motor connect
|
||||||
|
|
||||||
# Initialize the image map
|
# Initialize the image map
|
||||||
|
# self.init_motor_map()
|
||||||
|
|
||||||
|
def connect_motor(self, motor_x_name: str, motor_y_name: str):
|
||||||
|
self.motor_thread.connect_motors(motor_x_name, motor_y_name)
|
||||||
|
self.motor_thread.retrieve_motor_limits(self.motor_x, self.motor_y)
|
||||||
self.init_motor_map()
|
self.init_motor_map()
|
||||||
|
|
||||||
|
self.motorControl.setEnabled(True)
|
||||||
|
self.motorControl_absolute.setEnabled(True)
|
||||||
|
self.tabWidget_tables.setTabEnabled(1, True)
|
||||||
|
|
||||||
|
@pyqtSlot(object, object)
|
||||||
|
def get_selected_motors(self, motor_x, motor_y):
|
||||||
|
self.motor_x, self.motor_y = motor_x, motor_y
|
||||||
|
|
||||||
|
@pyqtSlot(list, list)
|
||||||
|
def get_available_motors(self, motors_x, motors_y):
|
||||||
|
self.comboBox_motor_x.addItems(motors_x)
|
||||||
|
self.comboBox_motor_y.addItems(motors_y)
|
||||||
|
print(f"got motors {motors_x} and {motors_y}")
|
||||||
|
|
||||||
@pyqtSlot(list, list)
|
@pyqtSlot(list, list)
|
||||||
def update_limits(self, x_limits: list, y_limits: list) -> None:
|
def update_limits(self, x_limits: list, y_limits: list) -> None:
|
||||||
self.limit_x = x_limits
|
self.limit_x = x_limits
|
||||||
@ -101,9 +128,12 @@ class MotorApp(QWidget):
|
|||||||
# 2D Plot
|
# 2D Plot
|
||||||
##########################
|
##########################
|
||||||
|
|
||||||
|
# self.label_coorditanes = self.glw.addLabel(
|
||||||
|
# f"Motor position: ({dev.samx.position():.2f}, {dev.samy.position():.2f})", row=0, col=0
|
||||||
|
# ) #TODO remove hardcoded samx and samy
|
||||||
self.label_coorditanes = self.glw.addLabel(
|
self.label_coorditanes = self.glw.addLabel(
|
||||||
f"Motor position: ({dev.samx.position():.2f}, {dev.samy.position():.2f})", row=0, col=0
|
f"Motor position: (X, Y)", row=0, col=0
|
||||||
)
|
) # TODO remove hardcoded samx and samy
|
||||||
self.plot_map = self.glw.addPlot(row=1, col=0)
|
self.plot_map = self.glw.addPlot(row=1, col=0)
|
||||||
self.limit_map = pg.ImageItem()
|
self.limit_map = pg.ImageItem()
|
||||||
self.plot_map.addItem(self.limit_map)
|
self.plot_map.addItem(self.limit_map)
|
||||||
@ -125,16 +155,16 @@ class MotorApp(QWidget):
|
|||||||
##########################
|
##########################
|
||||||
|
|
||||||
self.toolButton_right.clicked.connect(
|
self.toolButton_right.clicked.connect(
|
||||||
lambda: self.move_motor_relative(dev.samx, self.spinBox_step.value())
|
lambda: self.move_motor_relative(self.motor_x, self.spinBox_step.value())
|
||||||
)
|
)
|
||||||
self.toolButton_left.clicked.connect(
|
self.toolButton_left.clicked.connect(
|
||||||
lambda: self.move_motor_relative(dev.samx, -self.spinBox_step.value())
|
lambda: self.move_motor_relative(self.motor_x, -self.spinBox_step.value())
|
||||||
)
|
)
|
||||||
self.toolButton_up.clicked.connect(
|
self.toolButton_up.clicked.connect(
|
||||||
lambda: self.move_motor_relative(dev.samy, self.spinBox_step.value())
|
lambda: self.move_motor_relative(self.motor_y, self.spinBox_step.value())
|
||||||
)
|
)
|
||||||
self.toolButton_down.clicked.connect(
|
self.toolButton_down.clicked.connect(
|
||||||
lambda: self.move_motor_relative(dev.samy, -self.spinBox_step.value())
|
lambda: self.move_motor_relative(self.motor_y, -self.spinBox_step.value())
|
||||||
)
|
)
|
||||||
|
|
||||||
# Switch between key shortcuts active
|
# Switch between key shortcuts active
|
||||||
@ -188,10 +218,23 @@ class MotorApp(QWidget):
|
|||||||
precision=0,
|
precision=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Motor connections
|
||||||
|
self.pushButton_connecMotors.clicked.connect(
|
||||||
|
lambda: self.connect_motor(
|
||||||
|
self.comboBox_motor_x.currentText(), self.comboBox_motor_y.currentText()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if there are any motors connected
|
||||||
|
if self.motor_x or self.motor_y is None:
|
||||||
|
self.motorControl.setEnabled(False)
|
||||||
|
self.motorControl_absolute.setEnabled(False)
|
||||||
|
self.tabWidget_tables.setTabEnabled(1, False)
|
||||||
|
|
||||||
def init_motor_map(self):
|
def init_motor_map(self):
|
||||||
# Get motor limits
|
# Get motor limits
|
||||||
limit_x_min, limit_x_max = self.motor_thread.get_motor_limits(dev.samx)
|
limit_x_min, limit_x_max = self.motor_thread.get_motor_limits(self.motor_x)
|
||||||
limit_y_min, limit_y_max = self.motor_thread.get_motor_limits(dev.samy)
|
limit_y_min, limit_y_max = self.motor_thread.get_motor_limits(self.motor_y)
|
||||||
|
|
||||||
self.offset_x = limit_x_min
|
self.offset_x = limit_x_min
|
||||||
self.offset_y = limit_y_min
|
self.offset_y = limit_y_min
|
||||||
@ -241,9 +284,7 @@ class MotorApp(QWidget):
|
|||||||
|
|
||||||
self.motor_map.setData(pos=self.motor_positions, brush=self.brushes)
|
self.motor_map.setData(pos=self.motor_positions, brush=self.brushes)
|
||||||
|
|
||||||
def update_all_motor_limits(
|
def update_all_motor_limits(self, x_limit: list = None, y_limit: list = None) -> None:
|
||||||
self, x_limit: list = None, y_limit: list = None
|
|
||||||
) -> None: # TODO will be moved to thread
|
|
||||||
self.motor_thread.update_all_motor_limits(x_limit=x_limit, y_limit=y_limit)
|
self.motor_thread.update_all_motor_limits(x_limit=x_limit, y_limit=y_limit)
|
||||||
|
|
||||||
def update_arrow_key_shortcuts(self):
|
def update_arrow_key_shortcuts(self):
|
||||||
@ -311,6 +352,8 @@ class MotorControl(QThread):
|
|||||||
coordinates_updated = pyqtSignal(float, float) # Signal to emit current coordinates
|
coordinates_updated = pyqtSignal(float, float) # Signal to emit current coordinates
|
||||||
limits_retrieved = pyqtSignal(list, list) # Signal to emit current limits (samx, samy)
|
limits_retrieved = pyqtSignal(list, list) # Signal to emit current limits (samx, samy)
|
||||||
move_finished = pyqtSignal() # Signal to emit when the move is finished
|
move_finished = pyqtSignal() # Signal to emit when the move is finished
|
||||||
|
motors_loaded = pyqtSignal(list, list) # Signal to emit when the motors are loaded
|
||||||
|
motors_selected = pyqtSignal(object, object) # Signal to emit when the motors are selected
|
||||||
# progress_updated = pyqtSignal(int) #TODO Signal to emit progress percentage
|
# progress_updated = pyqtSignal(int) #TODO Signal to emit progress percentage
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -319,21 +362,50 @@ class MotorControl(QThread):
|
|||||||
parent=None,
|
parent=None,
|
||||||
):
|
):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
|
||||||
|
self.motor_x, self.motor_y = None, None
|
||||||
|
self.current_x, self.current_y = None, None
|
||||||
|
|
||||||
|
self.motors_consumer = None
|
||||||
|
|
||||||
|
# Get all available motors in the client
|
||||||
|
self.all_motors = self.get_all_motors()
|
||||||
|
self.all_motors_names = self.get_all_motors_names()
|
||||||
|
self.retrieve_all_motors() # send motor list to GUI
|
||||||
|
|
||||||
|
self.connect_motors("samx", "samy")
|
||||||
|
|
||||||
self.target_coordinates = None
|
self.target_coordinates = None
|
||||||
self.running = False
|
|
||||||
|
|
||||||
self.active_devices = active_devices
|
self.motor_x_debug = dev.samx
|
||||||
|
# self.all_motors = self.get_all_motors()
|
||||||
|
# self.all_motors_names = self.get_all_motors_names()
|
||||||
|
|
||||||
# Initialize current coordinates with the provided initial coordinates
|
# find index of samx in self.all_motors_names
|
||||||
(
|
# self.motor_x_index = self.all_motors_names.index(self.active_devices[0])
|
||||||
self.current_x,
|
|
||||||
self.current_y,
|
print(f"Motor debug: {self.motor_x_debug}")
|
||||||
) = self.get_coordinates() # TODO maybe not needed, coordinates always stored in redis
|
|
||||||
|
def motor_by_string(self, motor_x_name: str, motor_y_name: str) -> tuple:
|
||||||
|
motor_x_index = self.all_motors_names.index(motor_x_name)
|
||||||
|
motor_y_index = self.all_motors_names.index(motor_y_name)
|
||||||
|
|
||||||
|
motor_x = self.all_motors[motor_x_index]
|
||||||
|
motor_y = self.all_motors[motor_y_index]
|
||||||
|
return motor_x, motor_y
|
||||||
|
|
||||||
|
def connect_motors(self, motor_x_name: str, motor_y_name: str) -> None:
|
||||||
|
self.motor_x, self.motor_y = self.motor_by_string(motor_x_name, motor_y_name)
|
||||||
|
|
||||||
|
(self.current_x, self.current_y) = self.get_coordinates()
|
||||||
|
|
||||||
|
if self.motors_consumer is not None:
|
||||||
|
self.motors_consumer.shutdown()
|
||||||
|
|
||||||
self.motors_consumer = client.connector.consumer(
|
self.motors_consumer = client.connector.consumer(
|
||||||
topics=[
|
topics=[
|
||||||
MessageEndpoints.device_readback(self.active_devices[0]),
|
MessageEndpoints.device_readback(self.motor_x.name),
|
||||||
MessageEndpoints.device_readback(self.active_devices[1]),
|
MessageEndpoints.device_readback(self.motor_y.name),
|
||||||
],
|
],
|
||||||
cb=self._device_status_callback_motors,
|
cb=self._device_status_callback_motors,
|
||||||
parent=self,
|
parent=self,
|
||||||
@ -341,10 +413,29 @@ class MotorControl(QThread):
|
|||||||
|
|
||||||
self.motors_consumer.start()
|
self.motors_consumer.start()
|
||||||
|
|
||||||
|
self.motors_selected.emit(self.motor_x, self.motor_y)
|
||||||
|
|
||||||
|
def get_all_motors(self) -> list:
|
||||||
|
all_motors = client.device_manager.devices.acquisition_group("motor")
|
||||||
|
return all_motors
|
||||||
|
|
||||||
|
def get_all_motors_names(self) -> list:
|
||||||
|
all_motors = client.device_manager.devices.acquisition_group("motor")
|
||||||
|
all_motors_names = [motor.name for motor in all_motors]
|
||||||
|
return all_motors_names
|
||||||
|
|
||||||
|
def retrieve_all_motors(self):
|
||||||
|
self.all_motors = self.get_all_motors()
|
||||||
|
self.all_motors_names = self.get_all_motors_names()
|
||||||
|
self.motors_loaded.emit(self.all_motors_names, self.all_motors_names)
|
||||||
|
print("motors sent to GUI")
|
||||||
|
|
||||||
|
return self.all_motors, self.all_motors_names
|
||||||
|
|
||||||
def get_coordinates(self) -> tuple:
|
def get_coordinates(self) -> tuple:
|
||||||
"""Get current motor position"""
|
"""Get current motor position"""
|
||||||
x = dev.samx.read(cached=True)["value"] # TODO remove hardcoded samx and samy
|
x = self.motor_x.read(cached=True)["value"] # TODO remove hardcoded samx and samy
|
||||||
y = dev.samx.read(cached=True)["value"]
|
y = self.motor_y.read(cached=True)["value"]
|
||||||
return x, y
|
return x, y
|
||||||
|
|
||||||
def retrieve_coordinates(self) -> tuple:
|
def retrieve_coordinates(self) -> tuple:
|
||||||
@ -380,16 +471,16 @@ class MotorControl(QThread):
|
|||||||
raise ValueError("Current motor position is outside the new limits (X)")
|
raise ValueError("Current motor position is outside the new limits (X)")
|
||||||
else:
|
else:
|
||||||
self.update_motor_limits(
|
self.update_motor_limits(
|
||||||
dev.samx, low_limit=x_limit[0], high_limit=x_limit[1]
|
self.motor_x, low_limit=x_limit[0], high_limit=x_limit[1]
|
||||||
) # TODO Remove hardcoded samx and samy
|
) # TODO Remove hardcoded samx and samy
|
||||||
|
|
||||||
if y_limit is not None:
|
if y_limit is not None:
|
||||||
if current_position[1] < y_limit[0] or current_position[1] > y_limit[1]:
|
if current_position[1] < y_limit[0] or current_position[1] > y_limit[1]:
|
||||||
raise ValueError("Current motor position is outside the new limits (Y)")
|
raise ValueError("Current motor position is outside the new limits (Y)")
|
||||||
else:
|
else:
|
||||||
self.update_motor_limits(dev.samy, low_limit=y_limit[0], high_limit=y_limit[1])
|
self.update_motor_limits(self.motor_y, low_limit=y_limit[0], high_limit=y_limit[1])
|
||||||
|
|
||||||
self.retrieve_motor_limits(dev.samx, dev.samy)
|
self.retrieve_motor_limits(self.motor_x, self.motor_y)
|
||||||
|
|
||||||
def move_to_coordinates(self, target_coordinates: tuple):
|
def move_to_coordinates(self, target_coordinates: tuple):
|
||||||
self.action = "move_to_coordinates"
|
self.action = "move_to_coordinates"
|
||||||
@ -414,9 +505,9 @@ class MotorControl(QThread):
|
|||||||
def move_motor_coordinate(self) -> None:
|
def move_motor_coordinate(self) -> None:
|
||||||
"""Move the motor to the specified coordinates"""
|
"""Move the motor to the specified coordinates"""
|
||||||
status = scans.mv(
|
status = scans.mv(
|
||||||
dev.samx,
|
self.motor_x,
|
||||||
self.target_coordinates[0],
|
self.target_coordinates[0],
|
||||||
dev.samy,
|
self.motor_y,
|
||||||
self.target_coordinates[1],
|
self.target_coordinates[1],
|
||||||
relative=False,
|
relative=False,
|
||||||
)
|
)
|
||||||
@ -437,10 +528,10 @@ class MotorControl(QThread):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def _device_status_callback_motors(msg, *, parent, **_kwargs) -> None:
|
def _device_status_callback_motors(msg, *, parent, **_kwargs) -> None:
|
||||||
deviceMSG = BECMessage.DeviceMessage.loads(msg.value)
|
deviceMSG = BECMessage.DeviceMessage.loads(msg.value)
|
||||||
if parent.active_devices[0] in deviceMSG.content["signals"]:
|
if parent.motor_x.name in deviceMSG.content["signals"]:
|
||||||
parent.current_x = deviceMSG.content["signals"][parent.active_devices[0]]["value"]
|
parent.current_x = deviceMSG.content["signals"][parent.motor_x.name]["value"]
|
||||||
elif parent.active_devices[1] in deviceMSG.content["signals"]:
|
elif parent.motor_y.name in deviceMSG.content["signals"]:
|
||||||
parent.current_y = deviceMSG.content["signals"][parent.active_devices[1]]["value"]
|
parent.current_y = deviceMSG.content["signals"][parent.motor_y.name]["value"]
|
||||||
parent.coordinates_updated.emit(parent.current_x, parent.current_y)
|
parent.coordinates_updated.emit(parent.current_x, parent.current_y)
|
||||||
|
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user