-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Robot firefly #221
Robot firefly #221
Changes from 28 commits
ca577bd
935f6ba
1fbeeb6
fed655d
78e1bea
7c3e350
9c23e05
e3a4cd9
53bce11
6126070
3dab0bf
2c08ae3
997b33a
0680da2
ee5881f
f88e7f6
baf3e6d
41991ba
b6da5b7
b4170a8
9f93147
f69c33a
fcaba39
2577ae9
5ad1c1c
c120b9a
2d04ad2
c31912f
5ad31a6
c14573a
e4497e4
e9571b5
862491e
69b757c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,6 +49,10 @@ class FireflyApplication(PyDMApplication): | |
registry: Registry = None | ||
registry_changed = Signal(Registry) | ||
|
||
# For keeping track of ophyd devices used by the Firefly | ||
registry: Registry = None | ||
registry_changed = Signal(Registry) | ||
|
||
# Actions for showing window | ||
show_status_window_action: QtWidgets.QAction | ||
show_runs_window_action: QtWidgets.QAction | ||
|
@@ -57,6 +61,7 @@ class FireflyApplication(PyDMApplication): | |
show_voltmeters_window_action: QtWidgets.QAction | ||
show_logs_window_action: QtWidgets.QAction | ||
launch_queuemonitor_action: QtWidgets.QAction | ||
# show_robot_window_action: QtWidgets.QAction | ||
|
||
# Keep track of motors | ||
motor_actions: Sequence = [] | ||
|
@@ -241,6 +246,15 @@ def setup_window_actions(self): | |
ui_file="xrf_detector.py", | ||
device_key="DEV", | ||
) | ||
|
||
self._prepare_device_windows( | ||
device_label="robots", | ||
attr_name="robot", | ||
ui_file="robot.py", | ||
device_key="DEVICE", | ||
icon=qta.icon("mdi.robot-industrial"), | ||
) | ||
|
||
self._setup_window_action( | ||
action_name="show_filters_window_action", | ||
text="Filters", | ||
|
@@ -696,6 +710,12 @@ def show_sample_viewer_window(self): | |
FireflyMainWindow, ui_dir / "sample_viewer.ui", name="sample_viewer" | ||
) | ||
|
||
# @QtCore.Slot() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove if not needed. |
||
# def show_robot_window(self): | ||
# return self.show_window( | ||
# PlanMainWindow, ui_dir / "robot.py", name="robot" | ||
# ) | ||
|
||
@QtCore.Slot() | ||
def show_cameras_window(self): | ||
return self.show_window( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -140,6 +140,9 @@ def customize_ui(self): | |
text="Sample", | ||
menu=self.ui.positioners_menu, | ||
) | ||
# Robot transfer sample | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove if not needed. |
||
# self.ui.positioners_menu.addAction(app.show_robot_window_action) | ||
|
||
# Motors sub-menu | ||
self.ui.menuMotors = QtWidgets.QMenu(self.ui.menubar) | ||
self.ui.menuMotors.setObjectName("menuMotors") | ||
|
@@ -162,6 +165,9 @@ def customize_ui(self): | |
self.ui.positioners_menu.addSection("Tables") | ||
for action in app.table_actions.values(): | ||
self.ui.positioners_menu.addAction(action) | ||
self.ui.positioners_menu.addSection("Robots") | ||
for action in app.robot_actions.values(): | ||
self.ui.positioners_menu.addAction(action) | ||
# Scans menu | ||
self.ui.menuScans = QtWidgets.QMenu(self.ui.menubar) | ||
self.ui.menuScans.setObjectName("menuScans") | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
import logging | ||
|
||
from bluesky_queueserver_api import BPlan | ||
from qtpy import QtWidgets | ||
|
||
from firefly import display | ||
from firefly.component_selector import ComponentSelector | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
# ROBOT_NAMES = ["Austin"] | ||
|
||
SAMPLE_NUMBERS = [8, 9, 10, 14, 15, 16, 20, 21, 22, None] # list(range(24))+[None] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We shouldn't hard-code this list of which samples are enabled in the UI layer. Can we get this information from the |
||
|
||
|
||
class LineScanRegion: | ||
def __init__(self): | ||
self.setup_ui() | ||
|
||
def setup_ui(self): | ||
self.layout = QtWidgets.QHBoxLayout() | ||
|
||
# First item, motor No. | ||
# self.motor_label = QtWidgets.QLabel() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove if not needed. |
||
# self.motor_label.setText("1") | ||
# self.layout.addWidget(self.motor_label) | ||
|
||
# Second item, ComponentSelector | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "First item" |
||
self.motor_box = ComponentSelector() | ||
# self.stop_line_edit.setPlaceholderText("Stop…") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove if not needed. |
||
self.layout.addWidget(self.motor_box) | ||
|
||
# Third item, start point | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment does not describe the actual code. |
||
self.start_line_edit = QtWidgets.QLineEdit() | ||
self.start_line_edit.setPlaceholderText("Position…") | ||
self.layout.addWidget(self.start_line_edit) | ||
|
||
# Forth item, stop point | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove if not needed. |
||
# self.stop_line_edit = QtWidgets.QLineEdit() | ||
# self.stop_line_edit.setPlaceholderText("Stop…") | ||
# self.layout.addWidget(self.stop_line_edit) | ||
|
||
|
||
class RobotDisplay(display.FireflyDisplay): | ||
""" | ||
A GUI for managing sample tranfer using a robot plan: robot_sample(robot, number/None, motor1, position1, motor2, position2, motor3, position...) | ||
""" | ||
|
||
def customize_ui(self): | ||
self.reset_default_regions() | ||
# disable the line edits in spin box | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove if not needed. |
||
# self.ui.robot_combo_box.lineEdit().setReadOnly(True) | ||
# clear any exiting items in the combo box | ||
# self.ui.robot_combo_box.clear() | ||
# set the list of values for the combo box | ||
# for rbt in ROBOT_NAMES: | ||
# self.ui.robot_combo_box.addItem(rbt) | ||
|
||
# clear any exiting items in the combo box | ||
self.ui.sample_combo_box.clear() | ||
# set the list of values for the combo box | ||
for sam in SAMPLE_NUMBERS: | ||
self.ui.sample_combo_box.addItem(str(sam)) | ||
# self.ui.sample_combo_box.setCurrentIndex(0) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove if not needed. |
||
|
||
# disable the line edits in spin box | ||
canismarko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.ui.num_motor_spin_box.lineEdit().setReadOnly(True) | ||
self.ui.num_motor_spin_box.valueChanged.connect(self.update_regions) | ||
# self.ui.num_motor_spin_box.editingFinished.connect(self.update_regions) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove if not needed. |
||
|
||
# self.ui.run_button.setEnabled(True) #for testing | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove if not needed. |
||
self.ui.run_button.clicked.connect(self.queue_plan) | ||
|
||
def reset_default_regions(self): | ||
default_num_regions = 1 | ||
if not hasattr(self, "regions"): | ||
self.regions = [] | ||
self.add_regions(default_num_regions) | ||
canismarko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
self.ui.num_motor_spin_box.setValue(default_num_regions) | ||
self.update_regions() | ||
|
||
def add_regions(self, num=1): | ||
for i in range(num): | ||
region = LineScanRegion() | ||
self.ui.regions_layout.addLayout(region.layout) | ||
# Save it to the list | ||
self.regions.append(region) | ||
|
||
def remove_regions(self, num=1): | ||
for i in range(num): | ||
layout = self.regions[-1].layout | ||
# iterate/wait, and delete all widgets in the layout in the end | ||
while layout.count(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could this be |
||
item = layout.takeAt(0) | ||
if item.widget(): | ||
item.widget().deleteLater() | ||
self.regions.pop() | ||
|
||
def update_regions(self): | ||
new_region_num = self.ui.num_motor_spin_box.value() | ||
old_region_num = len(self.regions) | ||
diff_region_num = new_region_num - old_region_num | ||
|
||
if diff_region_num < 0: | ||
self.remove_regions(abs(diff_region_num)) | ||
elif diff_region_num > 0: | ||
self.add_regions(diff_region_num) | ||
|
||
def queue_plan(self, *args, **kwargs): | ||
"""Execute this plan on the queueserver.""" | ||
# Get scan parameters from widgets | ||
num_motor = self.ui.num_motor_spin_box.value() | ||
|
||
# Get the sample number from the sample_spin_box | ||
sam_num_str = self.ui.sample_combo_box.currentText() | ||
# Convert sam_num_str to an integer if it's a string representation of a number | ||
sam_num = int(sam_num_str) if sam_num_str.isdigit() else None | ||
|
||
# get parameters from motor regions | ||
motor_lst, position_lst = [], [] | ||
for region_i in self.regions: | ||
motor_lst.append(region_i.motor_box.current_component().name) | ||
position_lst.append(float(region_i.start_line_edit.text())) | ||
|
||
args = [ | ||
values for motor_i in zip(motor_lst, position_lst) for values in motor_i | ||
] | ||
|
||
# Build the queue item | ||
print(args) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove these print statements or them turn into logging calls. |
||
print(sam_num) | ||
robot = self.macros()["DEVICE"] | ||
print(robot) | ||
item = BPlan("robot_transfer_sample", robot, sam_num, *args) | ||
|
||
# Submit the item to the queueserver | ||
from firefly.application import FireflyApplication | ||
|
||
app = FireflyApplication.instance() | ||
log.info("Add ``robot_transfer_sample()`` plan to queue.") | ||
app.add_queue_item(item) | ||
|
||
def ui_filename(self): | ||
return "robot.ui" | ||
|
||
|
||
# ----------------------------------------------------------------------------- | ||
# :author: Yanna Chen | ||
# :email: [email protected] | ||
# :copyright: Copyright © 2024, UChicago Argonne, LLC | ||
# | ||
# Distributed under the terms of the 3-Clause BSD License | ||
# | ||
# The full licens is in the file LICENSE, distributed with this software. | ||
# | ||
# DISCLAIMER | ||
# | ||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
# | ||
# ----------------------------------------------------------------------------- |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be removed if not needed.