DAQ/GUI - Fluorescence spectrum in a single call

This commit is contained in:
2025-11-13 09:01:05 +01:00
parent 2021cf8cf0
commit 2fffeef0cf
6 changed files with 76 additions and 46 deletions
+8
View File
@@ -429,6 +429,14 @@ class BeamMarkCoeffModel(BaseModel):
y=self.coeff_y[0] * zoom**2 + self.coeff_y[1] * zoom + self.coeff_y[2],
)
class FluorescenceSpectrumParameterModel(BaseModel):
acq_time_s: float
class FluorescenceSpectrumOutputModel(BaseModel):
bkg: list[float] | None = None
spectrum: list[float]
energy_eV: list[float]
class MLBoxType(Enum):
Loop_all = 0
Pin = 1
+23 -1
View File
@@ -3,6 +3,7 @@ import json
import math
import statistics
import time
from asyncio import sleep
from datetime import datetime
from math import ceil
from typing import List, Tuple, Optional, Callable, Dict
@@ -34,7 +35,8 @@ from aaredaqlib.models import (
PuckLoadedInfo,
SampleShortInfoList,
DAQStatusModel, BeamlineStatus, SessionStatus, SampleCameraSettings, AutofocusSettings, BoundingBoxModel,
ZoomModeEnum, CrystalSize, SimpleScanParameters, MLBoxModel)
ZoomModeEnum, CrystalSize, SimpleScanParameters, MLBoxModel, FluorescenceSpectrumParameterModel,
FluorescenceSpectrumOutputModel)
from aaredaqlib.raster_grid import RasterGridRequest, CompletedRasterGrid, CompletedRasterGridElem
from aaredaqlib.rotation_scan import RotationScanRequest, CompletedRotationScan
from aaredaqlib.sample_geometry import SampleGeometryModel
@@ -1525,11 +1527,31 @@ class AareDAQ:
finally:
self.__cfg.state_busy = False
def fluorimeter_take_spectrum(self, fm: FluorescenceSpectrumParameterModel) -> FluorescenceSpectrumOutputModel:
self.__cfg.set_busy(BeamlineStateEnum.SampleAlignment)
try:
self.__devs.fluorimeter.start_acquisition(erase=True)
self.__devs.aerotech.set_shutter(1)
sleep(fm.acq_time_s)
self.__devs.aerotech.set_shutter(0)
self.__devs.fluorimeter.stop_acquisition()
spectrum = self.__devs.fluorimeter.get_current_data()
energy = [1.0 * i + 0.0 for i in range(len(spectrum))]
self.__cfg.state_busy = False
return FluorescenceSpectrumOutputModel(spectrum=spectrum,
bkg=self.__devs.fluorimeter.get_current_background(),
energy_eV=energy)
except Exception as e:
self.__cfg.state_busy = False
raise e
def fluorimeter_start(self, erase: bool = False):
self.__devs.aerotech.set_shutter(1)
self.__devs.fluorimeter.start_acquisition(erase=erase)
def fluorimeter_stop(self):
self.__devs.fluorimeter.stop_acquisition()
self.__devs.aerotech.set_shutter(0)
def fluorimeter_status(self) -> int | None:
return self.__devs.fluorimeter.check_status()
+8 -2
View File
@@ -10,7 +10,8 @@ from aaredaqlib.coordinate import SmargonCoordinate, Coordinate
from aaredaqlib.logger_config import setup_logger
from aaredaqlib.models import SampleShortInfo, DAQStatusModel, BeamlineStateEnum, BeamlineSettingsModel, \
SampleShortInfoList, SessionStatus, SampleCameraSettings, AutofocusSettings, TokenData, \
CryojetSettingsModel, SimpleScanParameters, CrystalSize
CryojetSettingsModel, SimpleScanParameters, CrystalSize, FluorescenceSpectrumParameterModel, \
FluorescenceSpectrumOutputModel
from aaredaqlib.raster_grid import RasterGridRequest, CompletedRasterGrid
from aaredaqlib.rotation_scan import RotationScanRequest, CompletedRotationScan
from aaredaqlib.sample_geometry import SampleGeometryModel
@@ -546,10 +547,15 @@ async def get_all_pgroups(token: str = Depends(oauth2_scheme)):
logger.error(f"Failed to list pgroups: {e}")
return []
@app.post("/fluorimeter/spectrum")
async def fluorimeter_spectrum(input: FluorescenceSpectrumParameterModel, token: str = Depends(oauth2_scheme)) -> FluorescenceSpectrumOutputModel:
auth.check_jwt_rw(cfg, auth.parse_token(token))
return daq.fluorimeter_take_spectrum(input)
@app.post("/fluorimeter/start")
async def fluorimeter_start(erase: bool = False, token: str = Depends(oauth2_scheme)) -> str:
auth.check_jwt_rw(cfg, auth.parse_token(token))
daq.fluorimeter_start(erase=erase)
daq.fluorimeter_start(erase)
return "OK"
@app.post("/fluorimeter/stop")
+2 -7
View File
@@ -356,13 +356,8 @@ class MainWindow(QMainWindow):
self.status_bar.get_all_pgroups.connect(self.daq.get_all_pgroups)
self.daq.staff_pgroups_loaded.connect(self.status_bar.staff_pgroups_loaded)
self.fluor_panel.start.connect(self.daq.fluorimeter_start)
self.fluor_panel.start_erase.connect(self.daq.fluorimeter_start_erase)
self.fluor_panel.stop.connect(self.daq.fluorimeter_stop)
self.fluor_panel.snapshot.connect(self.daq.fluorimeter_request_snapshot)
self.fluor_panel.live.connect(self.daq.fluorimeter_start_erase)
self.fluor_panel.live.connect(self.daq.start_fluorimeter_stream)
self.daq.fluorimeter_update.connect(self.fluor_panel.update_plot)
self.fluor_panel.spectrum.connect(self.daq.fluorimeter_spectrum)
self.daq.fluorimeter_spectrum_update.connect(self.fluor_panel.update_plot)
def create_menu_bar(self):
"""Create a menu bar with File->Quit and Help->About."""
+13 -35
View File
@@ -4,6 +4,7 @@ from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
import numpy as np
from aaredaqlib.models import FluorescenceSpectrumParameterModel, FluorescenceSpectrumOutputModel
from aaregui.widgets.number_line_edit import NumberLineEdit
from aaregui.widgets.title_label import TitleLabel
from aaredaqlib.logger_config import setup_logger
@@ -11,59 +12,36 @@ from aaredaqlib.logger_config import setup_logger
logger = setup_logger(__name__)
class FluorescencePanel(QWidget):
start = Signal()
start_erase = Signal()
stop = Signal()
snapshot = Signal()
live = Signal()
spectrum = Signal(FluorescenceSpectrumParameterModel)
def __init__(self, parent=None):
super().__init__(parent)
grid = QGridLayout(self)
grid.addWidget(TitleLabel("Fluorescence", self), 0, 0, 1, 4)
grid.addWidget(TitleLabel("Fluorescence", self), 0, 0)
self.btn_start = QPushButton("Start")
self.btn_start_erase = QPushButton("Erase+Start")
self.btn_stop = QPushButton("Stop")
self.btn_live = QPushButton("Start Live")
self.btn_snapshot = QPushButton("Snapshot")
for i, b in enumerate((self.btn_start, self.btn_start_erase, self.btn_stop, self.btn_live, self.btn_snapshot), start=1):
grid.addWidget(b, 1, i - 1)
self.btn_start = QPushButton("Spectrum")
grid.addWidget(self.btn_start, 1, 0)
self.fig = Figure(figsize=(5, 3), tight_layout=True)
self.ax = self.fig.add_subplot(111)
self.canvas = FigureCanvas(self.fig)
grid.addWidget(self.canvas, 2, 0, 1, 4)
grid.addWidget(self.canvas, 2, 0)
# Wire buttons
self.btn_start.clicked.connect(self.start.emit)
self.btn_start_erase.clicked.connect(self.start_erase.emit)
self.btn_stop.clicked.connect(self.stop.emit)
self.btn_snapshot.clicked.connect(self.snapshot.emit)
self.btn_live.clicked.connect(self.live.emit)
self.btn_start.clicked.connect(lambda: self.spectrum.emit(FluorescenceSpectrumParameterModel(acq_time_s=0.1)))
@Slot(list, list, int)
def update_plot(self, data, background, status):
@Slot(FluorescenceSpectrumOutputModel)
def update_plot(self, f: FluorescenceSpectrumOutputModel):
try:
self.ax.clear()
if data:
y = np.array(data, dtype=float)
x = np.arange(len(y), dtype=int)
if len(f.spectrum) > 0 and len(f.spectrum) == len(f.energy_eV):
x = np.array(f.energy_eV, dtype=float)
y = np.array(f.spectrum, dtype=float)
self.ax.plot(x, y, lw=1.0, label="Data")
if background:
b = np.array(background, dtype=float)
xb = np.arange(len(b), dtype=int)
self.ax.plot(xb, b, lw=1.0, label="Background")
self.ax.set_xlabel("Channel")
self.ax.set_xlabel("Energy eV")
self.ax.set_ylabel("Counts")
if self.ax.has_data():
self.ax.legend(loc="best")
self.ax.grid(True, alpha=0.3)
self.canvas.draw_idle()
except Exception as e:
+22 -1
View File
@@ -9,7 +9,7 @@ from jfjoch_client import ScanResult, ScanResultImagesInner
from aaredaqlib.coordinate import SmargonCoordinate, Coordinate
from aaredaqlib.models import DAQStatusModel, SampleShortInfoList, SampleShortInfo, SampleCameraSettings, \
AutofocusSettings, SimpleScanParameters
AutofocusSettings, SimpleScanParameters, FluorescenceSpectrumParameterModel, FluorescenceSpectrumOutputModel
from aaredaqlib.raster_grid import RasterGridRequest, CompletedRasterGrid
from aaredaqlib.rotation_scan import RotationScanRequest, CompletedRotationScan
@@ -34,6 +34,7 @@ class DAQWorker(QObject):
face_detection_result = Signal(dict)
staff_pgroups_loaded = Signal(list)
fluorimeter_update = Signal(list, list, int)
fluorimeter_spectrum_update = Signal(FluorescenceSpectrumOutputModel)
def __init__(self, base_url: str | None, token: str, parent=None):
super().__init__(parent)
@@ -560,6 +561,26 @@ class DAQWorker(QObject):
logger.error(f"Fluorimeter background error: {e}")
self.http_error.emit(str(e))
@Slot()
def fluorimeter_spectrum(self, f: FluorescenceSpectrumParameterModel):
if self.__base_url is None:
return
req = QNetworkRequest(QUrl(f"{self.__base_url}/fluorimeter/spectrum"))
req.setRawHeader(b"Authorization", f"Bearer {self.__token}".encode("utf-8"))
req.setRawHeader(b"Content-Type", b"application/json")
body = f.model_dump_json()
reply = self.__net_manager.post(req, QByteArray(body.encode("utf-8")))
reply.finished.connect(lambda: self._handle_fluorimeter_spectrum(reply))
def _handle_fluorimeter_spectrum(self, reply: QNetworkReply):
try:
response_data = self.handle_response(reply)
parsed_response = FluorescenceSpectrumOutputModel.model_validate_json(response_data)
self.fluorimeter_spectrum_update.emit(parsed_response)
except Exception as e:
logger.error(f"Exception from fluorimeter spectrum: {e}")
self.http_error.emit(str(e))
@Slot()
def fluorimeter_request_snapshot(self):
if self.__base_url is None: