Skip to content
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

Merged
merged 34 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ca577bd
create firefly GUI and script
yannachen Apr 3, 2024
935f6ba
Add motor list in GUI
yannachen Apr 3, 2024
1fbeeb6
Add robot GUI
yannachen Apr 5, 2024
fed655d
Merge branch 'spc-group:main' into robot_fire
yannachen Apr 8, 2024
78e1bea
update script
yannachen Apr 9, 2024
7c3e350
Add robot window to the main menu
yannachen Apr 9, 2024
9c23e05
use combo box instead of spin box
yannachen Apr 9, 2024
e3a4cd9
update instrument to skip firefly loading error
yannachen Apr 9, 2024
53bce11
Add motor region in GUI
yannachen Apr 10, 2024
6126070
Multiple motors work, but run_button does not work
yannachen Apr 10, 2024
3dab0bf
test file passes
yannachen Apr 11, 2024
2c08ae3
use macro for each robot
yannachen Apr 11, 2024
997b33a
update ui using macro
yannachen Apr 11, 2024
0680da2
update ui
yannachen Apr 12, 2024
ee5881f
stuck by macros
yannachen Apr 12, 2024
f88e7f6
Add mock_macros into the test file
yannachen Apr 12, 2024
baf3e6d
test for multiple robots and black
yannachen May 28, 2024
41991ba
Merge remote-tracking branch 'origin/main' into robot_fire
yannachen May 28, 2024
b6da5b7
Merge remote-tracking branch 'origin/main' into robot_fire
yannachen May 28, 2024
b4170a8
Update GUI after the firefly test
yannachen May 28, 2024
9f93147
Clean unused package
yannachen May 28, 2024
f69c33a
Merge branch 'main' into robot_fire
yannachen May 28, 2024
fcaba39
Test only one robot
yannachen May 29, 2024
2577ae9
Merge branch 'robot_fire' of github.com:yannachen/haven into robot_fire
yannachen May 29, 2024
5ad1c1c
Merge branch 'main' into robot_fire
canismarko May 30, 2024
c120b9a
Use longer LOAD_TIMEOUT
yannachen Jun 6, 2024
2d04ad2
Merge branch 'spc-group:main' into robot_fire
yannachen Jun 6, 2024
c31912f
Merge branch 'robot_fire' of github.com:yannachen/haven into robot_fire
yannachen Jun 6, 2024
5ad31a6
Merge branch 'spc-group:main' into robot_fire
yannachen Jun 14, 2024
c14573a
Update sample numbers depending on devices
yannachen Jun 17, 2024
e4497e4
Merge branch 'spc-group:main' into robot_fire
yannachen Jun 17, 2024
e9571b5
Isort the edited files
yannachen Jun 17, 2024
862491e
Merge branch 'robot_fire' of github.com:yannachen/haven into robot_fire
yannachen Jun 17, 2024
69b757c
Delete duplicate codes
yannachen Jun 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/firefly/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Copy link
Contributor

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.


# Keep track of motors
motor_actions: Sequence = []
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -696,6 +710,12 @@ def show_sample_viewer_window(self):
FireflyMainWindow, ui_dir / "sample_viewer.ui", name="sample_viewer"
)

# @QtCore.Slot()
Copy link
Contributor

Choose a reason for hiding this comment

The 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(
Expand Down
1 change: 1 addition & 0 deletions src/firefly/launcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ def main(default_fullscreen=False, default_display="status"):
app.setup_instrument()

# Get rid of the splash screen now that we're ready to go

splash.close()

event_loop.run_until_complete(app.start())
Expand Down
6 changes: 6 additions & 0 deletions src/firefly/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ def customize_ui(self):
text="Sample",
menu=self.ui.positioners_menu,
)
# Robot transfer sample
Copy link
Contributor

Choose a reason for hiding this comment

The 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")
Expand All @@ -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")
Expand Down
170 changes: 170 additions & 0 deletions src/firefly/robot.py
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]
Copy link
Contributor

Choose a reason for hiding this comment

The 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 Robot() device somehow?



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()
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"First item"

self.motor_box = ComponentSelector()
# self.stop_line_edit.setPlaceholderText("Stop…")
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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)
Copy link
Contributor

Choose a reason for hiding this comment

The 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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove if not needed.


# self.ui.run_button.setEnabled(True) #for testing
Copy link
Contributor

Choose a reason for hiding this comment

The 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():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be while layout.count() > 0? This current way should work okay, but checking explicitly for > 0 makes it more clear what you're thinking.

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)
Copy link
Contributor

Choose a reason for hiding this comment

The 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.
#
# -----------------------------------------------------------------------------
Loading