new_gui: manual sample creates a real DB entry via /sample/manual
Replace the GUI-local placeholder with the old GUI's flow: a dialog (custom name + optional unit cell) builds a SampleShortInfo(db_id=-1, ...) and posts it via DAQWorker.sample_manual -> POST /sample/manual, so the backend creates the DB entry, assigns the db_id and sets it as current sample. Cockpit then unlocks locally with the new sample (no robot mount; sample already on the gonio). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
02f33bd3fe
commit
bb55811781
@@ -26,8 +26,11 @@ panel — are **beam-path rails**: the completed length glows green and the acti
|
||||
station is a lavender focal bloom.
|
||||
|
||||
**Manual sample:** if a sample is on the goniometer by hand (not in the DB), use
|
||||
**⊕ Manual sample on gonio** in the changer footer — it unlocks the cockpit
|
||||
without driving the robot (no `mount`/`unmount` calls).
|
||||
**⊕ Manual sample on gonio** in the changer footer. It opens a dialog (custom
|
||||
name + optional unit cell), then `POST /sample/manual` (`DAQWorker.sample_manual`)
|
||||
so the backend creates a DB entry, assigns a `db_id` and sets it as the current
|
||||
sample — mirroring the old GUI. No robot mount happens (the sample is already on
|
||||
the gonio); the cockpit unlocks locally with the new sample.
|
||||
|
||||
**Offline dev mode:** with no server (`base_url is None`, e.g. `BEAMLINE=SIMULATED`
|
||||
and no `-u`), `dev_seed.py` injects a few fake samples + a synthetic live status +
|
||||
|
||||
@@ -304,6 +304,8 @@ class MainWindow(QWidget):
|
||||
self.manual.xrf_scan_requested.connect(self._do_xrf)
|
||||
self.manual.raster_goto_requested.connect(self.daq.move_smargon)
|
||||
self.manual.bookmark_goto_requested.connect(self._on_bookmark_goto)
|
||||
if hasattr(self.daq, "sample_manual"):
|
||||
self.manual.manual_sample_requested.connect(self.daq.sample_manual)
|
||||
self.manual.status_message.connect(self._note)
|
||||
if hasattr(self.daq, "run_number_incremented"):
|
||||
self.daq.run_number_incremented.connect(
|
||||
|
||||
@@ -40,6 +40,7 @@ class ManualView(QWidget):
|
||||
xrf_scan_requested = Signal(object) # FluorescenceSpectrumParameterModel
|
||||
raster_goto_requested = Signal(object) # SmargonCoordinate (goto a grid)
|
||||
bookmark_goto_requested = Signal(object, float) # SmargonCoordinate, omega_deg
|
||||
manual_sample_requested = Signal(object) # SampleShortInfo -> POST /sample/manual
|
||||
status_message = Signal(str) # transient note to surface in UI
|
||||
|
||||
def __init__(self, state: AppState, palette: Palette, defaults: dict, parent=None):
|
||||
@@ -137,7 +138,7 @@ class ManualView(QWidget):
|
||||
|
||||
# changer / mount CTAs
|
||||
self.changer.sample_selected.connect(s.mount_sample_obj)
|
||||
self.changer.manual_mount_requested.connect(s.manual_mount)
|
||||
self.changer.manual_mount_requested.connect(self._on_manual_mount)
|
||||
self.camera.mount_clicked.connect(s.open_picker)
|
||||
|
||||
# pipeline interactions
|
||||
@@ -311,6 +312,23 @@ class ManualView(QWidget):
|
||||
else:
|
||||
self.status_message.emit("Grid has no stored position to go to.")
|
||||
|
||||
# -------------------------------------------------- manual sample
|
||||
def _on_manual_mount(self) -> None:
|
||||
from PySide6.QtWidgets import QDialog
|
||||
from aare.gui.new_gui.widgets.manual_sample_dialog import ManualSampleDialog
|
||||
pgroup = "p16371"
|
||||
sess = getattr(self._last_status, "session", None)
|
||||
if sess is not None:
|
||||
pgroup = getattr(sess, "current_pgroup", None) or pgroup
|
||||
dlg = ManualSampleDialog(self._p, pgroup, self)
|
||||
if dlg.exec() != QDialog.Accepted:
|
||||
return
|
||||
sample = dlg.sample()
|
||||
# Tell the backend to create the DB entry + set it as current sample,
|
||||
# then unlock the cockpit locally with the new sample.
|
||||
self.manual_sample_requested.emit(sample)
|
||||
self._state.manual_mount(sample)
|
||||
|
||||
# ----------------------------------------------------- bookmarks
|
||||
def _refresh_bookmark_bar(self) -> None:
|
||||
self.pipeline.bookmarks.set_bookmarks([b["label"] for b in self._bookmarks])
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
"""Create a manual sample — one a user placed on the goniometer by hand.
|
||||
|
||||
Mirrors the old GUI's Manual Sample panel: a custom name (+ optional unit cell).
|
||||
On accept it yields a ``SampleShortInfo`` with ``db_id=-1``; the backend's
|
||||
``POST /sample/manual`` creates the DB entry, assigns the real id and makes it the
|
||||
current sample. No robot mount happens (the sample is already on the gonio).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtWidgets import (
|
||||
QCheckBox,
|
||||
QDialog,
|
||||
QDialogButtonBox,
|
||||
QDoubleSpinBox,
|
||||
QGridLayout,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QSpinBox,
|
||||
QVBoxLayout,
|
||||
)
|
||||
|
||||
from aare.common.models import SampleShortInfo
|
||||
from aare.gui.new_gui.theme import build_qss
|
||||
|
||||
|
||||
class ManualSampleDialog(QDialog):
|
||||
def __init__(self, palette, pgroup: str = "p16371", parent=None):
|
||||
super().__init__(parent)
|
||||
self._pgroup = pgroup or "p16371"
|
||||
self.setWindowTitle("Manual sample on goniometer")
|
||||
self.setModal(True)
|
||||
self.setStyleSheet(build_qss(palette))
|
||||
self.setMinimumWidth(320)
|
||||
|
||||
root = QVBoxLayout(self)
|
||||
intro = QLabel("Register a sample you placed on the goniometer by hand.")
|
||||
intro.setWordWrap(True)
|
||||
intro.setStyleSheet(f"color:{palette.text_muted}; font-size:12px;")
|
||||
root.addWidget(intro)
|
||||
|
||||
grid = QGridLayout()
|
||||
grid.setColumnStretch(1, 1)
|
||||
grid.addWidget(QLabel("Name"), 0, 0)
|
||||
self._name = QLineEdit("Lyso")
|
||||
grid.addWidget(self._name, 0, 1, 1, 2)
|
||||
|
||||
self._unit_cell = QCheckBox("Provide unit cell")
|
||||
grid.addWidget(self._unit_cell, 1, 1, 1, 2)
|
||||
|
||||
self._a = self._num(5, 1000, 39.0)
|
||||
self._b = self._num(5, 1000, 78.0)
|
||||
self._c = self._num(5, 1000, 78.0)
|
||||
self._alpha = self._num(30, 330, 90.0)
|
||||
self._beta = self._num(30, 330, 90.0)
|
||||
self._gamma = self._num(30, 330, 90.0)
|
||||
for row, (lbl, w, unit) in enumerate((
|
||||
("a", self._a, "Å"), ("b", self._b, "Å"), ("c", self._c, "Å"),
|
||||
("α", self._alpha, "°"), ("β", self._beta, "°"), ("γ", self._gamma, "°"),
|
||||
), start=2):
|
||||
grid.addWidget(QLabel(lbl), row, 0)
|
||||
grid.addWidget(w, row, 1)
|
||||
grid.addWidget(QLabel(unit), row, 2)
|
||||
grid.addWidget(QLabel("SG"), 8, 0)
|
||||
self._sg = QSpinBox()
|
||||
self._sg.setRange(1, 250)
|
||||
self._sg.setValue(1)
|
||||
grid.addWidget(self._sg, 8, 1)
|
||||
root.addLayout(grid)
|
||||
|
||||
buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||
buttons.button(QDialogButtonBox.Ok).setText("Create + mount")
|
||||
buttons.accepted.connect(self.accept)
|
||||
buttons.rejected.connect(self.reject)
|
||||
root.addWidget(buttons)
|
||||
|
||||
self._unit_cell.toggled.connect(self._toggle_cell)
|
||||
self._toggle_cell(False)
|
||||
|
||||
def _num(self, lo: float, hi: float, val: float) -> QDoubleSpinBox:
|
||||
s = QDoubleSpinBox()
|
||||
s.setRange(lo, hi)
|
||||
s.setDecimals(2)
|
||||
s.setValue(val)
|
||||
return s
|
||||
|
||||
def _toggle_cell(self, on: bool) -> None:
|
||||
for w in (self._a, self._b, self._c, self._alpha, self._beta,
|
||||
self._gamma, self._sg):
|
||||
w.setEnabled(on)
|
||||
|
||||
def sample(self) -> SampleShortInfo:
|
||||
params = None
|
||||
if self._unit_cell.isChecked():
|
||||
from aareDB import DataCollectionParameters
|
||||
params = DataCollectionParameters(
|
||||
spacegroupnumber=int(self._sg.value()),
|
||||
cellparameters=(
|
||||
f"{self._a.value():.2f} {self._b.value():.2f} "
|
||||
f"{self._c.value():.2f} {self._alpha.value():.2f} "
|
||||
f"{self._beta.value():.2f} {self._gamma.value():.2f}"),
|
||||
)
|
||||
return SampleShortInfo(
|
||||
db_id=-1, puck_name="", dewar_name="",
|
||||
sample_name=self._name.text().strip() or "Manual sample",
|
||||
run_number=1, pin=1, aaredb_params=params, user=self._pgroup,
|
||||
)
|
||||
Reference in New Issue
Block a user