diff --git a/common/src/aaredaqlib/models.py b/common/src/aaredaqlib/models.py index f38ff15c..e4c2ac22 100644 --- a/common/src/aaredaqlib/models.py +++ b/common/src/aaredaqlib/models.py @@ -133,6 +133,10 @@ class SessionsStateEnum(Enum): OwnedByElse = 2 Vacant = 3 +class SampleCameraSettings(BaseModel): + gain: float + exposure: float + class BeamlineStatus(BaseModel): energy_keV: float dtz_mm: float @@ -141,6 +145,7 @@ class BeamlineStatus(BaseModel): cryojet_K: float shutter_open: bool flux_ph_s: float + sample_camera: SampleCameraSettings class SessionStatus(BaseModel): session: SessionsStateEnum = SessionsStateEnum.Vacant diff --git a/daq/src/aaredaq/daq.py b/daq/src/aaredaq/daq.py index e7ea5672..3b463c50 100644 --- a/daq/src/aaredaq/daq.py +++ b/daq/src/aaredaq/daq.py @@ -10,7 +10,7 @@ from aaredaqlib.models import ( SampleShortInfo, PuckLoadedInfo, SampleShortInfoList, - DAQStatusModel, BeamlineStatus, SessionStatus, ) + DAQStatusModel, BeamlineStatus, SessionStatus, SampleCameraSettings, ) from aaredaqlib.raster_grid import RasterGridRequest from aaredaqlib.rotation_scan import RotationScanRequest from aaredaqlib.sample_geometry import SampleGeometryModel @@ -109,6 +109,14 @@ class AareDAQ: return None return self.__cfg.current_sample + @property + def samcam_settings(self) -> SampleCameraSettings: + return self.__devs.samcam_settings + + @samcam_settings.setter + def samcam_settings(self, s: SampleCameraSettings): + self.__devs.samcam_settings = s + def tweak_abr_meas_pos(self, c: Coordinate): self.__cfg.set_busy(BeamlineStateEnum.SampleAlignment) try: @@ -287,7 +295,7 @@ class AareDAQ: self.__devs.lamp_light.move("on", wait=True) for s in self.__cfg.alc_zoom_settings.z: - self.__devs.sample_cam.setup(expo=s.sam_cam_exp, gain=s.sam_cam_gain) + self.__devs.samcam_settings = SampleCameraSettings(exposure=s.sam_cam_exp, gain=s.sam_cam_gain) self.__devs.zoom = s.zoom_value time.sleep(5.0) # Wait for settings to stabilize @@ -462,7 +470,7 @@ class AareDAQ: try: i = 0 for s in self.__cfg.alc_zoom_settings.z: - self.__devs.sample_cam.setup(expo=s.sam_cam_exp, gain=s.sam_cam_gain) + self.__devs.samcam_settings = SampleCameraSettings(exposure=s.sam_cam_exp, gain=s.sam_cam_gain) self.__devs.zoom_sync(s.zoom_value) if i % 2 == 0: angles = (0, 90) @@ -686,7 +694,8 @@ class AareDAQ: light=self.light, cryojet_K=self.__devs.cryojet_temp, shutter_open=self.__devs.shutter, - flux_ph_s=3.0e12 + flux_ph_s=3.0e12, + sample_camera=self.__devs.samcam_settings ) @property diff --git a/daq/src/aaredaq/devices.py b/daq/src/aaredaq/devices.py index bda5da8f..e2d28186 100644 --- a/daq/src/aaredaq/devices.py +++ b/daq/src/aaredaq/devices.py @@ -2,6 +2,7 @@ from aaredaqlib.coordinate import Coordinate from epics import PV from aaredaqlib.beamline import MXBeamline +from aaredaqlib.models import SampleCameraSettings from mxlibs3 import aerotech, smargon from mxlibs3.area_detector import epicsAD from mxlibs3.enum_pv import EnumPv @@ -236,3 +237,15 @@ class BeamlineDevices: self.__shutter.put(1) else: self.__shutter.put(0) + + @property + def samcam_settings(self) -> SampleCameraSettings: + return SampleCameraSettings( + gain=self.sample_cam.gain.value, + exposure=self.sample_cam.expo.value + ) + + @samcam_settings.setter + def samcam_settings(self, settings: SampleCameraSettings): + print(settings) + self.sample_cam.setup(settings.gain, settings.exposure) \ No newline at end of file diff --git a/daq/src/aaredaq/server.py b/daq/src/aaredaq/server.py index 2ed08ab1..fefbc657 100644 --- a/daq/src/aaredaq/server.py +++ b/daq/src/aaredaq/server.py @@ -6,7 +6,7 @@ import urllib3 import uvicorn from aaredaqlib.coordinate import SmargonCoordinate, Coordinate from aaredaqlib.models import SampleShortInfo, DAQStatusModel, BeamlineStateEnum, BeamlineSettingsModel, \ - SampleShortInfoList, SessionStatus + SampleShortInfoList, SessionStatus, SampleCameraSettings from aaredaqlib.raster_grid import RasterGridRequest from aaredaqlib.rotation_scan import RotationScanRequest from aaredaqlib.sample_geometry import SampleGeometryModel @@ -111,6 +111,11 @@ async def clear_beam_center(token: str = Depends(oauth2_scheme)): daq.clear_mark_beam() return "OK" +@app.put("/beamline/samcam") +async def samcam_settings(s: SampleCameraSettings, token: str = Depends(oauth2_scheme)): + auth.check_jwt_staff(auth.parse_token(token)) + daq.samcam_settings = s + return "OK" @app.post("/shutter/close") async def close_shutter(token: str = Depends(oauth2_scheme)): diff --git a/gui/src/aaregui/main_window.py b/gui/src/aaregui/main_window.py index af5de197..931a832b 100644 --- a/gui/src/aaregui/main_window.py +++ b/gui/src/aaregui/main_window.py @@ -113,6 +113,8 @@ class MainWindow(QMainWindow): self.daq.update.connect(self.tell_samples.update_daq_status) self.daq.update.connect(self.beamline.abr_tweak.update_daq_status) self.daq.update.connect(self.status_bar.update_daq_status) + self.daq.update.connect(self.beamline.samcam.update_daq_status) + self.beamline.samcam.changed.connect(self.daq.samcam_settings) self.beamline.state_panel.sample_exchange.clicked.connect(self.daq.sample_exchange) self.beamline.state_panel.sample_alignment.clicked.connect(self.daq.sample_alignment) self.beamline.state_panel.dewar_exchange.clicked.connect(self.daq.dewar_exchange) diff --git a/gui/src/aaregui/panels/beamline_controls.py b/gui/src/aaregui/panels/beamline_controls.py index 03911a2e..146b0542 100644 --- a/gui/src/aaregui/panels/beamline_controls.py +++ b/gui/src/aaregui/panels/beamline_controls.py @@ -6,6 +6,7 @@ from aaregui.panels.beamline_state_panel import BeamlineStatePanel from aaregui.panels.illumination_panel import IlluminationPanel from aaregui.panels.loop_centering_panel import LoopCenteringPanel from aaregui.panels.omega_panel import OmegaPanel +from aaregui.panels.samcam_panel import SamcamPanel from aaregui.panels.smargon_panel import SmargonPanel from aaregui.panels.zoom_panel import ZoomPanel @@ -38,6 +39,9 @@ class BeamlineControls(QFrame): self.state_panel = BeamlineStatePanel(self) self.v_layout.addWidget(self.state_panel) + self.samcam = SamcamPanel(self) + self.v_layout.addWidget(self.samcam) + self.abr_tweak = AbrTweakWidget(self) self.beam_center = BeamCenterWidget(self) @@ -47,3 +51,4 @@ class BeamlineControls(QFrame): self.v_layout.addStretch() self.setLayout(self.v_layout) + diff --git a/gui/src/aaregui/panels/samcam_panel.py b/gui/src/aaregui/panels/samcam_panel.py new file mode 100644 index 00000000..d6789c47 --- /dev/null +++ b/gui/src/aaregui/panels/samcam_panel.py @@ -0,0 +1,61 @@ +from PySide6.QtWidgets import QWidget, QSpinBox, QVBoxLayout, QHBoxLayout, QLabel, QDoubleSpinBox +from PySide6.QtCore import Signal, Slot + +from aaredaqlib.models import SampleCameraSettings, DAQStatusModel +from aaregui.widgets.title_label import TitleLabel + + +class SamcamPanel(QWidget): + # Signals for when exposure or gain values change + changed = Signal(SampleCameraSettings) + + old_settings = SampleCameraSettings(gain=100, exposure=0.001) + + def __init__(self, parent=None): + super().__init__(parent) + + # Create layout + layout = QVBoxLayout() + + layout.addWidget(TitleLabel("Sample camera", self)) + + # Exposure control + exposure_layout = QHBoxLayout() + exposure_label = QLabel("Exposure (s):") + self.exposure_spinbox = QDoubleSpinBox() + self.exposure_spinbox.setRange(0, 1.0) # Adjust range as needed + self.exposure_spinbox.setSingleStep(0.001) + self.exposure_spinbox.setStyleSheet("QDoubleSpinBox { background-color: white; }") + self.exposure_spinbox.setDecimals(3) + self.exposure_spinbox.editingFinished.connect(self.__changed) + exposure_layout.addWidget(exposure_label) + exposure_layout.addWidget(self.exposure_spinbox) + + # Gain control + gain_layout = QHBoxLayout() + gain_label = QLabel("Gain:") + self.gain_spinbox = QDoubleSpinBox() + self.gain_spinbox.setRange(0, 1000) # Adjust range as needed + self.gain_spinbox.setSingleStep(1) + self.gain_spinbox.setDecimals(1) + self.gain_spinbox.setStyleSheet("QDoubleSpinBox { background-color: white; }") + self.gain_spinbox.editingFinished.connect(self.__changed) + gain_layout.addWidget(gain_label) + gain_layout.addWidget(self.gain_spinbox) + + # Add controls to main layout + layout.addLayout(exposure_layout) + layout.addLayout(gain_layout) + self.setLayout(layout) + + def __changed(self): + self.changed.emit(SampleCameraSettings(gain=self.gain_spinbox.value(), + exposure=self.exposure_spinbox.value())) + + @Slot(DAQStatusModel) + def update_daq_status(self, s: DAQStatusModel): + pass + if self.old_settings != s.bl.sample_camera: + self.gain_spinbox.setValue(s.bl.sample_camera.gain) + self.exposure_spinbox.setValue(s.bl.sample_camera.exposure) + self.old_settings = s.bl.sample_camera diff --git a/gui/src/aaregui/threads/daq_worker.py b/gui/src/aaregui/threads/daq_worker.py index 73061572..1628fd33 100644 --- a/gui/src/aaregui/threads/daq_worker.py +++ b/gui/src/aaregui/threads/daq_worker.py @@ -1,7 +1,7 @@ from PySide6.QtCore import Signal, QUrl, Slot, QTimer, QObject, QByteArray from PySide6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply -from aaredaqlib.models import DAQStatusModel, SampleShortInfoList, SampleShortInfo +from aaredaqlib.models import DAQStatusModel, SampleShortInfoList, SampleShortInfo, SampleCameraSettings from aaredaqlib.coordinate import SmargonCoordinate, Coordinate from aaredaqlib.raster_grid import RasterGridRequest from aaredaqlib.rotation_scan import RotationScanRequest @@ -153,6 +153,10 @@ class DAQWorker(QObject): else: self.generic_put(f"access/pgroup?val={val}") + @Slot(SampleCameraSettings) + def samcam_settings(self, s: SampleCameraSettings): + self.generic_put("beamline/samcam", s.model_dump_json()) + @Slot(SmargonCoordinate) def move_smargon(self, coord: SmargonCoordinate): self.generic_put("beamline/smargon", coord.model_dump_json())