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 33 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
13 changes: 13 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 Down Expand Up @@ -250,6 +254,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
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(load_instrument=not pydm_args.no_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
3 changes: 3 additions & 0 deletions src/firefly/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,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
147 changes: 147 additions & 0 deletions src/firefly/robot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import logging

from bluesky_queueserver_api import BPlan
from qtpy import QtWidgets

from firefly import display
from firefly.application import FireflyApplication
from firefly.component_selector import ComponentSelector

log = logging.getLogger(__name__)


class LineScanRegion:
def __init__(self):
self.setup_ui()

def setup_ui(self):
self.layout = QtWidgets.QHBoxLayout()

# ComponentSelector
self.motor_box = ComponentSelector()
self.layout.addWidget(self.motor_box)

# Motor position for robot transferring samples
self.start_line_edit = QtWidgets.QLineEdit()
self.start_line_edit.setPlaceholderText("Position…")
self.layout.addWidget(self.start_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 sample_numbers(self):
sample_names = [name for name, device in self.device.samples.walk_subdevices()]
sample_numbers = [int(name.replace("sample", "")) for name in sample_names]
sample_numbers.append(None)
return sample_numbers

def customize_ui(self):
self.reset_default_regions()
# clear any exiting items in the combo box
self.ui.sample_combo_box.clear()
# Get the list of sample numbers
sample_numbers = self.sample_numbers()
# set the list of values for the combo box
for sam in sample_numbers:
self.ui.sample_combo_box.addItem(str(sam))

# 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.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() > 0:
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
robot = self.macros()["DEVICE"]
item = BPlan("robot_transfer_sample", robot, sam_num, *args)

# Submit the item to the queueserver
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
Loading