mirror of
https://github.com/bec-project/bec_widgets.git
synced 2025-07-13 19:21:50 +02:00
feat: motor selection
This commit is contained in:
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1115</width>
|
||||
<height>506</height>
|
||||
<width>1120</width>
|
||||
<height>547</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -43,6 +43,12 @@
|
||||
</property>
|
||||
</widget>
|
||||
</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">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
@ -50,11 +56,12 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="comboBox"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="comboBox_2"/>
|
||||
<item row="3" column="0" colspan="2">
|
||||
<widget class="QPushButton" name="pushButton_connecMotors">
|
||||
<property name="text">
|
||||
<string>Connect Motors</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -208,7 +215,7 @@
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<widget class="QGroupBox" name="motorControl_absolute">
|
||||
<property name="title">
|
||||
<string>Move Absolute</string>
|
||||
</property>
|
||||
@ -271,7 +278,7 @@
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab">
|
||||
<widget class="QWidget" name="tab_coordinates">
|
||||
<attribute name="title">
|
||||
<string>Coordinates</string>
|
||||
</attribute>
|
||||
@ -307,7 +314,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<widget class="QWidget" name="tab_settings">
|
||||
<attribute name="title">
|
||||
<string>Settings</string>
|
||||
</attribute>
|
||||
@ -510,7 +517,7 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<widget class="QWidget" name="tab_queue">
|
||||
<attribute name="title">
|
||||
<string>Queue</string>
|
||||
</attribute>
|
||||
|
@ -31,6 +31,10 @@ class MotorApp(QWidget):
|
||||
current_path = os.path.dirname(__file__)
|
||||
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
|
||||
|
||||
# Coordinates tracking
|
||||
@ -39,21 +43,44 @@ class MotorApp(QWidget):
|
||||
self.num_dim_points = 15 # Number of points to dim gradually
|
||||
|
||||
# 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)
|
||||
|
||||
# UI
|
||||
self.init_ui()
|
||||
self.tag_N = 1 # position label for saved coordinates
|
||||
|
||||
# Initialize current coordinates with the provided initial coordinates
|
||||
self.motor_thread.retrieve_motor_limits(dev.samx, dev.samy)
|
||||
# Get all motors available
|
||||
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
|
||||
# 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.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)
|
||||
def update_limits(self, x_limits: list, y_limits: list) -> None:
|
||||
self.limit_x = x_limits
|
||||
@ -101,9 +128,12 @@ class MotorApp(QWidget):
|
||||
# 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(
|
||||
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.limit_map = pg.ImageItem()
|
||||
self.plot_map.addItem(self.limit_map)
|
||||
@ -125,16 +155,16 @@ class MotorApp(QWidget):
|
||||
##########################
|
||||
|
||||
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(
|
||||
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(
|
||||
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(
|
||||
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
|
||||
@ -188,10 +218,23 @@ class MotorApp(QWidget):
|
||||
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):
|
||||
# Get motor limits
|
||||
limit_x_min, limit_x_max = self.motor_thread.get_motor_limits(dev.samx)
|
||||
limit_y_min, limit_y_max = self.motor_thread.get_motor_limits(dev.samy)
|
||||
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(self.motor_y)
|
||||
|
||||
self.offset_x = limit_x_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)
|
||||
|
||||
def update_all_motor_limits(
|
||||
self, x_limit: list = None, y_limit: list = None
|
||||
) -> None: # TODO will be moved to thread
|
||||
def update_all_motor_limits(self, x_limit: list = None, y_limit: list = None) -> None:
|
||||
self.motor_thread.update_all_motor_limits(x_limit=x_limit, y_limit=y_limit)
|
||||
|
||||
def update_arrow_key_shortcuts(self):
|
||||
@ -311,6 +352,8 @@ class MotorControl(QThread):
|
||||
coordinates_updated = pyqtSignal(float, float) # Signal to emit current coordinates
|
||||
limits_retrieved = pyqtSignal(list, list) # Signal to emit current limits (samx, samy)
|
||||
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
|
||||
|
||||
def __init__(
|
||||
@ -319,21 +362,50 @@ class MotorControl(QThread):
|
||||
parent=None,
|
||||
):
|
||||
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.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
|
||||
(
|
||||
self.current_x,
|
||||
self.current_y,
|
||||
) = self.get_coordinates() # TODO maybe not needed, coordinates always stored in redis
|
||||
# find index of samx in self.all_motors_names
|
||||
# self.motor_x_index = self.all_motors_names.index(self.active_devices[0])
|
||||
|
||||
print(f"Motor debug: {self.motor_x_debug}")
|
||||
|
||||
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(
|
||||
topics=[
|
||||
MessageEndpoints.device_readback(self.active_devices[0]),
|
||||
MessageEndpoints.device_readback(self.active_devices[1]),
|
||||
MessageEndpoints.device_readback(self.motor_x.name),
|
||||
MessageEndpoints.device_readback(self.motor_y.name),
|
||||
],
|
||||
cb=self._device_status_callback_motors,
|
||||
parent=self,
|
||||
@ -341,10 +413,29 @@ class MotorControl(QThread):
|
||||
|
||||
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:
|
||||
"""Get current motor position"""
|
||||
x = dev.samx.read(cached=True)["value"] # TODO remove hardcoded samx and samy
|
||||
y = dev.samx.read(cached=True)["value"]
|
||||
x = self.motor_x.read(cached=True)["value"] # TODO remove hardcoded samx and samy
|
||||
y = self.motor_y.read(cached=True)["value"]
|
||||
return x, y
|
||||
|
||||
def retrieve_coordinates(self) -> tuple:
|
||||
@ -380,16 +471,16 @@ class MotorControl(QThread):
|
||||
raise ValueError("Current motor position is outside the new limits (X)")
|
||||
else:
|
||||
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
|
||||
|
||||
if y_limit is not None:
|
||||
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)")
|
||||
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):
|
||||
self.action = "move_to_coordinates"
|
||||
@ -414,9 +505,9 @@ class MotorControl(QThread):
|
||||
def move_motor_coordinate(self) -> None:
|
||||
"""Move the motor to the specified coordinates"""
|
||||
status = scans.mv(
|
||||
dev.samx,
|
||||
self.motor_x,
|
||||
self.target_coordinates[0],
|
||||
dev.samy,
|
||||
self.motor_y,
|
||||
self.target_coordinates[1],
|
||||
relative=False,
|
||||
)
|
||||
@ -437,10 +528,10 @@ class MotorControl(QThread):
|
||||
@staticmethod
|
||||
def _device_status_callback_motors(msg, *, parent, **_kwargs) -> None:
|
||||
deviceMSG = BECMessage.DeviceMessage.loads(msg.value)
|
||||
if parent.active_devices[0] in deviceMSG.content["signals"]:
|
||||
parent.current_x = deviceMSG.content["signals"][parent.active_devices[0]]["value"]
|
||||
elif parent.active_devices[1] in deviceMSG.content["signals"]:
|
||||
parent.current_y = deviceMSG.content["signals"][parent.active_devices[1]]["value"]
|
||||
if parent.motor_x.name in deviceMSG.content["signals"]:
|
||||
parent.current_x = deviceMSG.content["signals"][parent.motor_x.name]["value"]
|
||||
elif parent.motor_y.name in deviceMSG.content["signals"]:
|
||||
parent.current_y = deviceMSG.content["signals"][parent.motor_y.name]["value"]
|
||||
parent.coordinates_updated.emit(parent.current_x, parent.current_y)
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user