diff --git a/tomcat_bec/device_configs/microxas_test_bed.yaml b/tomcat_bec/device_configs/microxas_test_bed.yaml index 5c282e7..1fef45e 100644 --- a/tomcat_bec/device_configs/microxas_test_bed.yaml +++ b/tomcat_bec/device_configs/microxas_test_bed.yaml @@ -94,7 +94,7 @@ femto_mean_curr: gf2: description: GigaFrost camera controls - deviceClass: tomcat_bec.devices.gigafrost.gigafrostclient.GigaFrostClient + deviceClass: tomcat_bec.devices.gigafrost.gigafrostcamera.GigaFrostCamera deviceConfig: prefix: 'X02DA-CAM-GF2:' backend_url: 'http://xbl-daq-28:8080' @@ -106,6 +106,23 @@ gf2: readOnly: false readoutPriority: monitored softwareTrigger: true + +#gf2: +# description: GigaFrost camera controls +# deviceClass: tomcat_bec.devices.gigafrost.gigafrostclient.GigaFrostClient +# deviceConfig: +# prefix: 'X02DA-CAM-GF2:' +# backend_url: 'http://xbl-daq-28:8080' +# auto_soft_enable: true +# deviceTags: +# - camera +# enabled: true +# onFailure: buffer +# readOnly: false +# readoutPriority: monitored +# softwareTrigger: true + + daq: description: Standard DAQ controls deviceClass: tomcat_bec.devices.gigafrost.stddaq_ws.StdDaqWsClient diff --git a/tomcat_bec/devices/gigafrost/gfconstants.py b/tomcat_bec/devices/gigafrost/gfconstants.py index f7786bf..e0eab37 100644 --- a/tomcat_bec/devices/gigafrost/gfconstants.py +++ b/tomcat_bec/devices/gigafrost/gfconstants.py @@ -6,7 +6,7 @@ Created on Thu Jun 27 17:28:43 2024 @author: mohacsi_i """ -from enum import Enum +from enum import IntEnum gf_valid_enable_modes = ("soft", "external", "soft+ext", "always") @@ -16,7 +16,7 @@ gf_valid_fix_nframe_modes = ("off", "start", "end", "start+end") # STATUS -class GfStatus(Enum): +class GfStatus(IntEnum): """Operation states for GigaFrost Ophyd device""" NEW = 1 INITIALIZED = 2 diff --git a/tomcat_bec/devices/gigafrost/gigafrostcamera.py b/tomcat_bec/devices/gigafrost/gigafrostcamera.py index a63b217..9f5e5ee 100644 --- a/tomcat_bec/devices/gigafrost/gigafrostcamera.py +++ b/tomcat_bec/devices/gigafrost/gigafrostcamera.py @@ -6,9 +6,8 @@ Created on Thu Jun 27 17:28:43 2024 @author: mohacsi_i """ -import sys from time import sleep -from ophyd import Device, Component, EpicsSignal, EpicsSignalRO, Kind, DeviceStatus +from ophyd import Signal, SignalRO, Device, Component, EpicsSignal, EpicsSignalRO, Kind, DeviceStatus from ophyd.device import Staged from ophyd_devices.interfaces.base_classes.psi_detector_base import ( @@ -26,6 +25,13 @@ try: except ModuleNotFoundError: from tomcat_bec.devices.gigafrost.gfutils import extend_header_table +try: + from bec_lib import bec_logger + logger = bec_logger.logger +except ModuleNotFoundError: + import logging + logger = logging.getLogger("GfCam") + class GigaFrostCameraMixin(CustomDetectorMixin): """Mixin class to setup TOMCAT specific implementations of the detector. @@ -38,7 +44,7 @@ class GigaFrostCameraMixin(CustomDetectorMixin): elif self.parent.backendUrl.get() == const.BE999_DAFL_CLIENT: return const.BE999_NORTH_IP, const.BE999_SOUTH_IP else: - raise RuntimeError(f"Backend not recognized. {(const.GF1, const.GF2, const.GF3)}") + raise RuntimeError(f"Backend {self.parent.backendUrl.get()} not recognized. {(const.GF1, const.GF2, const.GF3)}") def _define_backend_mac(self): if self.parent.backendUrl.get() == const.BE3_DAFL_CLIENT: # xbl-daq-33 @@ -46,7 +52,7 @@ class GigaFrostCameraMixin(CustomDetectorMixin): elif self.parent.backendUrl.get() == const.BE999_DAFL_CLIENT: return const.BE999_NORTH_MAC, const.BE999_SOUTH_MAC else: - raise RuntimeError(f"Backend not recognized. {(const.GF1, const.GF2, const.GF3)}") + raise RuntimeError(f"Backend {self.parent.backendUrl.get()} not recognized. {(const.GF1, const.GF2, const.GF3)}") def _set_udp_header_table(self): """Set the communication parameters for the camera module""" @@ -62,11 +68,11 @@ class GigaFrostCameraMixin(CustomDetectorMixin): source_port = 3000 + j if j < 4: extend_header_table( - udp_header_table, self.parent.macSouth, self.parent.ipSouth, dest_port, source_port + udp_header_table, self.parent.macSouth.get(), self.parent.ipSouth.get(), dest_port, source_port ) else: extend_header_table( - udp_header_table, self.parent.macNorth, self.parent.ipNorth, dest_port, source_port + udp_header_table, self.parent.macNorth.get(), self.parent.ipNorth.get(), dest_port, source_port ) return udp_header_table @@ -88,7 +94,7 @@ class GigaFrostCameraMixin(CustomDetectorMixin): self.parent.cmdWriteService.set(1).wait() # Configure software triggering if needed - if self.parent._auto_soft_enable: + if self.parent.autoSoftEnable.get(): # trigger modes self.parent.cfgCntStartBit.set(1).wait() self.parent.cfgCntEndBit.set(0).wait() @@ -120,15 +126,15 @@ class GigaFrostCameraMixin(CustomDetectorMixin): self.parent.state.put(const.GfStatus.INIT, force=True) return super().on_init() - - def on_stage(self) -> None: - """ - Specify actions to be executed during stage in preparation for a scan. - self.parent.scaninfo already has all current parameters for the upcoming scan. + """Specify actions to be executed during stage - In case the backend service is writing data on disk, this step should include publishing - a file_event and file_message to BEC to inform the system where the data is written to. + Specifies actions to be executed during the stage step in preparation + for a scan. self.parent.scaninfo already has all current parameters for + the upcoming scan. + + The gigafrost camera IOC does not write data to disk, that's done by + the DAQ ophyd device. IMPORTANT: It must be safe to assume that the device is ready for the scan @@ -143,8 +149,7 @@ class GigaFrostCameraMixin(CustomDetectorMixin): self.parent._staged = Staged.no def on_unstage(self) -> None: - """ - Specify actions to be executed during unstage. + """Specify actions to be executed during unstage. This step should include checking if the acqusition was successful, and publishing the file location and file event message, @@ -153,7 +158,7 @@ class GigaFrostCameraMixin(CustomDetectorMixin): # Switch to idle self.parent.cmdStartCamera.set(0).wait() if self.parent.autoSoftEnable.get(): - self.cmdSoftEnable.set(0).wait() + self.parent.cmdSoftEnable.set(0).wait() self.parent.state.put(const.GfStatus.STOPPED, force=True) def on_stop(self) -> None: @@ -180,7 +185,7 @@ class GigaFrostCameraMixin(CustomDetectorMixin): sleep_time = self.parent.cfgFramerate.value*self.parent.cfgCntNum.value*0.001+0.050 # There's no status readback from the camera, so we just wait sleep(sleep_time) - print(f"[GF2] Slept for: {sleep_time} seconds", file=sys.stderr) + logger.info(f"[GF2] Slept for: {sleep_time} seconds") else: self.parent.cmdSoftTrigger.set(1).wait() status.set_finished() @@ -209,13 +214,12 @@ class GigaFrostCamera(PSIDetectorBase): Bugs: ---------- - FRAMERATE : Ignored in soft trigger mode, period becomes 2xexposure time + FRAMERATE : Ignored in soft trigger mode, period becomes 2xExposure time """ # pylint: disable=too-many-instance-attributes custom_prepare_cls = GigaFrostCameraMixin - - + USER_ACCESS = [""] infoBusyFlag = Component(EpicsSignalRO, "BUSY_STAT", auto_monitor=True) infoSyncFlag = Component(EpicsSignalRO, "SYNC_FLAG", auto_monitor=True) @@ -358,9 +362,13 @@ class GigaFrostCamera(PSIDetectorBase): USER_ACCESS = ["exposure_mode", "fix_nframes_mode", "trigger_mode", "enable_mode"] - autoSoftEnable = Component(Signal, auto_monitor=True, kind=Kind.config) - backendUrl = Component(Signal, auto_monitor=True, kind=Kind.config) - state = Component(Signal, auto_monitor=True, kind=Kind.config) + autoSoftEnable = Component(Signal, kind=Kind.config) + backendUrl = Component(Signal, kind=Kind.config) + macNorth = Component(Signal, kind=Kind.config) + macSouth = Component(Signal, kind=Kind.config) + ipNorth = Component(Signal, kind=Kind.config) + ipSouth = Component(Signal, kind=Kind.config) + state = Component(Signal, value=int(const.GfStatus.NEW), kind=Kind.config) def __init__( self, @@ -376,14 +384,11 @@ class GigaFrostCamera(PSIDetectorBase): parent=None, **kwargs, ): - # Additional parameters - self.autoSoftEnable._metadata["write_access"] = False - self.backendUrl._metadata["write_access"] = False - self.state._metadata["write_access"] = False - self.autoSoftEnable.put(auto_soft_enable, force=True) - self.backendUrl.put(backend_url, force=True) - self.state.put(const.GfStatus.NEW, force=True) - + # Ugly hack to pass values before on_init() + self._signals_to_be_set = {} + self._signals_to_be_set['auto_soft_enable'] = auto_soft_enable + self._signals_to_be_set['backend_url'] = backend_url + # super() will call the mixin class super().__init__( prefix=prefix, @@ -395,6 +400,17 @@ class GigaFrostCamera(PSIDetectorBase): **kwargs, ) + def _init(self): + """Ugly hack: values must be set before on_init() is called""" + # Additional parameters + self.autoSoftEnable._metadata["write_access"] = False + self.backendUrl._metadata["write_access"] = False + self.state._metadata["write_access"] = False + self.autoSoftEnable.put(self._signals_to_be_set['auto_soft_enable'], force=True) + self.backendUrl.put(self._signals_to_be_set['backend_url'], force=True) + self.state.put(const.GfStatus.NEW, force=True) + return super()._init() + def configure( self, nimages=10, @@ -448,7 +464,7 @@ class GigaFrostCamera(PSIDetectorBase): # Stop acquisition self.cmdStartCamera.set(0).wait() - if self._auto_soft_enable: + if self.autoSoftEnable.get(): self.cmdSoftEnable.set(0).wait() # change settings