From fbfa562713adaf374dfaf67ebf30cbd1895dd428 Mon Sep 17 00:00:00 2001 From: e21206 Date: Wed, 30 Aug 2023 22:27:47 +0200 Subject: [PATCH] fix: online changes to integrate devices in BEC --- .../epics/devices/DelayGeneratorDG645.py | 17 ++++++- ophyd_devices/epics/devices/__init__.py | 5 ++ .../epics/devices/bec_scaninfo_mixin.py | 25 ++++++---- ophyd_devices/epics/devices/eiger9m_csaxs.py | 33 +++++++------ ophyd_devices/epics/devices/falcon_csaxs.py | 47 ++++++++++++++++--- ophyd_devices/epics/devices/mcs_csaxs.py | 14 ++++-- ophyd_devices/epics/devices/pilatus_csaxs.py | 6 +++ ophyd_devices/epics/devices/specMotors.py | 2 +- ophyd_devices/galil/sgalil_ophyd.py | 6 +-- ophyd_devices/utils/bec_utils.py | 10 +++- 10 files changed, 122 insertions(+), 43 deletions(-) diff --git a/ophyd_devices/epics/devices/DelayGeneratorDG645.py b/ophyd_devices/epics/devices/DelayGeneratorDG645.py index ab3a316..54e6002 100644 --- a/ophyd_devices/epics/devices/DelayGeneratorDG645.py +++ b/ophyd_devices/epics/devices/DelayGeneratorDG645.py @@ -334,7 +334,8 @@ class DelayGeneratorDG645(Device): self._producer = bec_utils.MockProducer() self.device_manager = bec_utils.MockDeviceManager() self.scaninfo = BecScaninfoMixin(device_manager, sim_mode) - self.all_channels = ["channelT0", "channelAB", "channelCD", "channelEF", "channelGH"] + self._all_channels = ["channelT0", "channelAB", "channelCD", "channelEF", "channelGH"] + self._all_delay_pairs = ["AB", "CD", "EF", "GH"] self.wait_for_connection() # Make sure to be connected before talking to PVs self._init_ddg() self._ddg_is_okay() @@ -386,7 +387,7 @@ class DelayGeneratorDG645(Device): def _set_channels(self, signal: str, value: Any, channels: List = None) -> None: if not channels: - channels = self.all_channels + channels = self._all_channels for chname in channels: channel = getattr(self, chname, None) if not channel: @@ -404,6 +405,17 @@ class DelayGeneratorDG645(Device): self._init_ddg_pol_allchannels(self.polarity.get()) self._init_ddg_amp_allchannels(self.amplitude.get()) self._init_ddg_offset_allchannels(self.offset.get()) + self._set_channels( + "reference", + 0, + [f"channel{self._all_delay_pairs[ii]}.ch1" for ii in range(len(self._all_delay_pairs))], + ) + for ii in range(len(self._all_delay_pairs)): + self._set_channels( + "reference", + 2 * ii + 1, + [f"channel{self._all_delay_pairs[ii]}.ch2"], + ) self._set_trigger(TriggerSource.SINGLE_SHOT) self.level.set(self.thres_trig_level.get()) @@ -474,6 +486,7 @@ class DelayGeneratorDG645(Device): # if self.scaninfo.scan_type == "step": if self.source.read()[self.source.name]["value"] == int(TriggerSource.SINGLE_SHOT): self.trigger_shot.set(1).wait() + super().trigger() def burstEnable(self, count, delay, period, config="all"): """Enable the burst mode""" diff --git a/ophyd_devices/epics/devices/__init__.py b/ophyd_devices/epics/devices/__init__.py index c65ce0b..d1e599e 100644 --- a/ophyd_devices/epics/devices/__init__.py +++ b/ophyd_devices/epics/devices/__init__.py @@ -21,3 +21,8 @@ from .specMotors import ( from ophyd import EpicsSignal, EpicsSignalRO, EpicsMotor from ophyd.sim import SynAxis, SynSignal, SynPeriodicSignal from ophyd.quadem import QuadEM + +# cSAXS +from .mcs_csaxs import McsCsaxs +from .eiger9m_csaxs import Eiger9mCsaxs +from .pilatus_csaxs import PilatusCsaxs diff --git a/ophyd_devices/epics/devices/bec_scaninfo_mixin.py b/ophyd_devices/epics/devices/bec_scaninfo_mixin.py index b9b3ac3..d09c66a 100644 --- a/ophyd_devices/epics/devices/bec_scaninfo_mixin.py +++ b/ophyd_devices/epics/devices/bec_scaninfo_mixin.py @@ -7,6 +7,21 @@ class BecScaninfoMixin: def __init__(self, device_manager: DeviceManagerBase = None, sim_mode=False) -> None: self.device_manager = device_manager self.sim_mode = sim_mode + self.bec_info_msg = { + "RID": "mockrid", + "queueID": "mockqueuid", + "scan_number": 1, + "exp_time": 26e-3, + "num_points": 10000, + "readout_time": 2e-3, + "scan_type": "fly", + } + + def get_bec_info_msg(self) -> None: + return self.bec_info_msg + + def change_config(self, bec_info_msg: dict) -> None: + self.bec_info_msg = bec_info_msg def _get_current_scan_msg(self) -> BECMessage.ScanStatusMessage: if not self.sim_mode: @@ -16,15 +31,7 @@ class BecScaninfoMixin: return BECMessage.ScanStatusMessage( scanID="1", status={}, - info={ - "RID": "mockrid", - "queueID": "mockqueuid", - "scan_number": 1, - "exp_time": 26e-3, - "num_points": 10000, - "readout_time": 2e-3, - "scan_type": "fly", - }, + info=self.bec_info_msg, ) def _get_username(self) -> str: diff --git a/ophyd_devices/epics/devices/eiger9m_csaxs.py b/ophyd_devices/epics/devices/eiger9m_csaxs.py index 99f8d89..0f4d042 100644 --- a/ophyd_devices/epics/devices/eiger9m_csaxs.py +++ b/ophyd_devices/epics/devices/eiger9m_csaxs.py @@ -4,7 +4,7 @@ from typing import Any, List import numpy as np from ophyd import EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV -from ophyd import CamBase, DetectorBase +from ophyd import CamBase, DetectorBase, Device from ophyd import ADComponent as ADCpt from ophyd.areadetector.plugins import FileBase @@ -25,7 +25,7 @@ class EigerError(Exception): pass -class SlsDetectorCam(CamBase, FileBase): +class SlsDetectorCam(Device): # CamBase, FileBase): detector_type = ADCpt(EpicsSignalRO, "DetectorType_RBV") setting = ADCpt(EpicsSignalWithRBV, "Setting") delay_time = ADCpt(EpicsSignalWithRBV, "DelayTime") @@ -63,6 +63,10 @@ class SlsDetectorCam(CamBase, FileBase): json_frame_mode = ADCpt(EpicsSignalWithRBV, "JsonFrameMode") json_detector_mode = ADCpt(EpicsSignalWithRBV, "JsonDetectorMode") + # fixes due to missing PVs from CamBase + acquire = ADCpt(EpicsSignal, "Acquire") + detector_state = ADCpt(EpicsSignalRO, "DetectorState_RBV") + class TriggerSource(int, enum.Enum): AUTO = 0 @@ -127,8 +131,8 @@ class Eiger9mCsaxs(DetectorBase): self.name = name self.wait_for_connection() # Make sure to be connected before talking to PVs if not sim_mode: - self._producer = self.device_manager.producer self.device_manager = device_manager + self._producer = self.device_manager.producer else: self._producer = bec_utils.MockProducer() self.device_manager = bec_utils.MockDeviceManager() @@ -139,7 +143,6 @@ class Eiger9mCsaxs(DetectorBase): self.filewriter = FileWriterMixin(self.service_cfg) self.reduce_readout = 1e-3 # 3 ms self.triggermode = 0 # 0 : internal, scan must set this if hardware triggered - self.mokev = 12 self._init_eiger9m() self._init_standard_daq() @@ -149,7 +152,6 @@ class Eiger9mCsaxs(DetectorBase): def _init_eiger9m(self) -> None: """Init parameters for Eiger 9m""" - self._set_det_threshold() self._set_trigger(TriggerSource.GATING) self.cam.acquire.set(0) @@ -184,7 +186,7 @@ class Eiger9mCsaxs(DetectorBase): def _prep_det(self) -> None: self._set_det_threshold() self._set_acquisition_params() - self._set_trigger(TriggerSource.TRIGGER) + self._set_trigger(TriggerSource.GATING) def _set_det_threshold(self) -> None: # threshold_energy PV exists on Eiger 9M? @@ -226,7 +228,6 @@ class Eiger9mCsaxs(DetectorBase): self.std_client.start_writer_async( {"output_file": self.filepath, "n_images": self.scaninfo.num_frames} ) - logger.info("Waiting for std daq to be armed") while True: det_ctrl = self.std_client.get_status()["acquisition"]["state"] if det_ctrl == "WAITING_IMAGES": @@ -240,16 +241,18 @@ class Eiger9mCsaxs(DetectorBase): def stage(self) -> List[object]: """stage the detector and file writer""" self.scaninfo.load_scan_metadata() - self.mokev = self.device_manager.devices.mokev.read()[ + self.mokev = self.device_manager.devices.mokev.obj.read()[ self.device_manager.devices.mokev.name ]["value"] self._prep_det() + logger.info("Waiting for std daq to be armed") self._prep_file_writer() + logger.info("std_daq is ready") msg = BECMessage.FileMessage(file_path=self.filepath, done=False) self._producer.set_and_publish( - MessageEndpoints.public_file(self.scaninfo.scanID, "eiger9m"), + MessageEndpoints.public_file(self.scaninfo.scanID, self.name), msg.dumps(), ) self.arm_acquisition() @@ -279,13 +282,13 @@ class Eiger9mCsaxs(DetectorBase): break time.sleep(0.005) # Message to BEC - # state = True + state = True - # msg = BECMessage.FileMessage(file_path=self.filepath, done=True, successful=state) - # self._producer.set_and_publish( - # MessageEndpoints.public_file(self.metadata["scanID"], self.name), - # msg.dumps(), - # ) + msg = BECMessage.FileMessage(file_path=self.filepath, done=True, successful=state) + self._producer.set_and_publish( + MessageEndpoints.public_file(self.scaninfo.scanID, self.name), + msg.dumps(), + ) return super().unstage() def arm_acquisition(self) -> None: diff --git a/ophyd_devices/epics/devices/falcon_csaxs.py b/ophyd_devices/epics/devices/falcon_csaxs.py index 372ad68..508d072 100644 --- a/ophyd_devices/epics/devices/falcon_csaxs.py +++ b/ophyd_devices/epics/devices/falcon_csaxs.py @@ -9,6 +9,9 @@ from ophyd.areadetector.plugins import HDF5Plugin, HDF5Plugin_V21, FilePlugin_V2 from bec_lib.core.file_utils import FileWriterMixin from bec_lib.core import MessageEndpoints, BECMessage, RedisConnector from bec_lib.core import bec_logger +from ophyd_devices.epics.devices.bec_scaninfo_mixin import BecScaninfoMixin + +from ophyd_devices.utils import bec_utils logger = bec_logger.logger @@ -34,6 +37,10 @@ class EpicsDXPFalcon(Device): current_pixel = Cpt(EpicsSignalRO, "CurrentPixel") +class FalconError(Exception): + pass + + class FalconHDF5Plugins(HDF5Plugin_V21, FilePlugin_V22): pass @@ -80,6 +87,7 @@ class FalconCsaxs(Device): configuration_attrs=None, parent=None, device_manager=None, + sim_mode=False, **kwargs, ): super().__init__( @@ -91,15 +99,23 @@ class FalconCsaxs(Device): parent=parent, **kwargs, ) - self.device_manager = device_manager - self.name = name - self.username = "e21206" - # TODO once running from BEC - # self.username = self.device_manager.producer.get(MessageEndpoints.account()).decode() + if device_manager is None and not sim_mode: + raise FalconError("Add DeviceManager to initialization or init with sim_mode=True") - self.service_cfg = {"base_path": f"/sls/X12SA/data/{self.username}/Data10/data/"} + self.name = name + self.wait_for_connection() # Make sure to be connected before talking to PVs + if not sim_mode: + self.device_manager = device_manager + self._producer = self.device_manager.producer + else: + self._producer = bec_utils.MockProducer() + self.device_manager = bec_utils.MockDeviceManager() + self.scaninfo = BecScaninfoMixin(device_manager, sim_mode) + # TODO + self.scaninfo.username = "e21206" + self.service_cfg = {"base_path": f"/sls/X12SA/data/{self.scaninfo.username}/Data10/"} self.filewriter = FileWriterMixin(self.service_cfg) - self._producer = RedisConnector(["localhost:6379"]).producer() + self.readout = 0.003 # 3 ms self._value_pixel_per_buffer = 16 # TODO create file template from filewriter compile filename @@ -218,3 +234,20 @@ class FalconCsaxs(Device): # TODO raise error logger.warning("Returned in unknown state") return state + + def stop(self, *, success=False) -> None: + """Stop acquisition + Stop or Stop and Erase + """ + self._clean_up.set(1) + # self.erase_all.set(1) + self.unstage() + super().stop(success=success) + self._stopped = True + + # Automatically connect to test environmenr if directly invoked + + +if __name__ == "__main__": + falcon = FalconCsaxs(name="falcon", prefix="X12SA-SITORO::", sim_mode=True) + falcon.stage() diff --git a/ophyd_devices/epics/devices/mcs_csaxs.py b/ophyd_devices/epics/devices/mcs_csaxs.py index 9a59302..af8bd21 100644 --- a/ophyd_devices/epics/devices/mcs_csaxs.py +++ b/ophyd_devices/epics/devices/mcs_csaxs.py @@ -154,8 +154,8 @@ class McsCsaxs(SIS38XX): self.name = name self.wait_for_connection() # Make sure to be connected before talking to PVs if not sim_mode: - self._producer = self.device_manager.producer self.device_manager = device_manager + self._producer = self.device_manager.producer else: self._producer = bec_utils.MockProducer() self.device_manager = bec_utils.MockDeviceManager() @@ -164,6 +164,7 @@ class McsCsaxs(SIS38XX): self.scaninfo.username = "e21206" self.service_cfg = {"base_path": f"/sls/X12SA/data/{self.scaninfo.username}/Data10/"} self.filewriter = FileWriterMixin(self.service_cfg) + self._stopped = False self._init_mcs() def _init_mcs(self) -> None: @@ -202,11 +203,12 @@ class McsCsaxs(SIS38XX): """Set readout mode of mcs card Check ReadoutMode class for more information about options """ + # self.read_mode.set(ReadoutMode.EVENT) self.read_mode.set(ReadoutMode.PASSIVE) def _read_mcs_card(self) -> None: # TODO how to properly trigger the readout!!! - self.read_all.set(1) + self.read_all.put(1, use_complete=False) def readout_data(self) -> List: self._read_mcs_card() @@ -250,8 +252,11 @@ class McsCsaxs(SIS38XX): det_ctrl = self.acquiring.read()[self.acquiring.name]["value"] if det_ctrl == 0: break + if self._stopped: + break time.sleep(0.005) - self._read_mcs_card() + if not self._stopped: + self._read_mcs_card() # Message to BEC # state = True @@ -260,6 +265,7 @@ class McsCsaxs(SIS38XX): # MessageEndpoints.public_file(self.metadata["scanID"], self.name), # msg.dumps(), # ) + self._stopped = False return super().unstage() def arm_acquisition(self) -> None: @@ -277,7 +283,7 @@ class McsCsaxs(SIS38XX): """ self.stop_all.set(1) # self.erase_all.set(1) - self.unstage() + # self.unstage() super().stop(success=success) self._stopped = True diff --git a/ophyd_devices/epics/devices/pilatus_csaxs.py b/ophyd_devices/epics/devices/pilatus_csaxs.py index 782e828..74b5557 100644 --- a/ophyd_devices/epics/devices/pilatus_csaxs.py +++ b/ophyd_devices/epics/devices/pilatus_csaxs.py @@ -248,3 +248,9 @@ class PilatusCsaxs(DetectorBase): self.unstage() super().stop(success=success) self._stopped = True + + +# Automatically connect to test environmenr if directly invoked +if __name__ == "__main__": + pilatus_2 = PilatusCsaxs(name="pilatus_2", prefix="X12SA-ES-PILATUS300K:", sim_mode=True) + pilatus_2.stage() diff --git a/ophyd_devices/epics/devices/specMotors.py b/ophyd_devices/epics/devices/specMotors.py index dde88fe..81f8270 100644 --- a/ophyd_devices/epics/devices/specMotors.py +++ b/ophyd_devices/epics/devices/specMotors.py @@ -181,7 +181,7 @@ class MonoTheta2(VirtualEpicsSignalRO): MONO_THETA2_OFFSETS_FILENAME = ( - "/import/work/sls/spec/local/X12SA/macros/spec_data/mono_th2_offsets.txt" + "/sls/X12SA/data/gac-x12saop/spec/macros/spec_data/mono_th2_offsets.txt" ) diff --git a/ophyd_devices/galil/sgalil_ophyd.py b/ophyd_devices/galil/sgalil_ophyd.py index e1a35a4..9400ae4 100644 --- a/ophyd_devices/galil/sgalil_ophyd.py +++ b/ophyd_devices/galil/sgalil_ophyd.py @@ -247,6 +247,7 @@ class GalilController(Controller): interval_x: int, exp_time: float, readtime: float, + **kwargs, ) -> tuple: """_summary_ @@ -266,9 +267,8 @@ class GalilController(Controller): LimitError: Raised if the speed is above 2mm/s or below 0.02mm/s """ - - # time.sleep(0.2) - + # + axes_referenced = self.controller.axis_is_referenced() # Check limits # TODO check sign of stage, or not necessary check_values = [start_y, end_y, start_x, end_x] diff --git a/ophyd_devices/utils/bec_utils.py b/ophyd_devices/utils/bec_utils.py index d30200d..02a8b4f 100644 --- a/ophyd_devices/utils/bec_utils.py +++ b/ophyd_devices/utils/bec_utils.py @@ -16,9 +16,15 @@ class MockDeviceManager: self.devices = devices() +class OphydObject: + def __init__(self) -> None: + self.name = "mock_mokev" + self.obj = mokev() + + class devices: def __init__(self): - self.mokev = mokev() + self.mokev = OphydObject() class mokev: @@ -26,4 +32,4 @@ class mokev: self.name = "mock_mokev" def read(self): - return {self.name: {"value": 12.4, "timestamp": time.time()}} + return {self.name: {"value": 16.0, "timestamp": time.time()}}