0
0
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:
wyzula-jan
2023-08-25 16:07:05 +02:00
parent 0226188079
commit cab32be009
2 changed files with 144 additions and 46 deletions

View File

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

View File

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