feat: add falcon and progress bar option to devices

This commit is contained in:
appel_c 2023-09-07 11:07:59 +02:00
parent 3a126976cd
commit 3bab432a2f
8 changed files with 316 additions and 212 deletions

View File

@ -134,6 +134,10 @@ class DelayGeneratorDG645(Device):
current device
"""
SUB_PROGRESS = "progress"
SUB_VALUE = "value"
_default_sub = SUB_VALUE
USER_ACCESS = [
"set_channels",
"_set_trigger",
@ -403,7 +407,7 @@ class DelayGeneratorDG645(Device):
LINE = 6
"""
value = int(trigger_source)
self.source.set(value)
self.source.put(value)
def _ddg_is_okay(self, raise_on_error=False) -> None:
status = self.status.read()[self.status.name]["value"]
@ -452,14 +456,14 @@ class DelayGeneratorDG645(Device):
)
self._set_trigger(getattr(TriggerSource, self.set_trigger_source.get()))
# Set threshold level for ext. pulses
self.level.set(self.thres_trig_level.get())
self.level.put(self.thres_trig_level.get())
def _check_burst_cycle(self, status) -> None:
"""Checks burst cycle of delay generator
Force readout, return value from end of burst cycle
"""
while True:
self.trigger_burst_readout.set(1)
self.trigger_burst_readout.put(1)
if (
self.burst_cycle_finished.read()[self.burst_cycle_finished.name]["value"] == 1
and self.delay_finished.read()[self.delay_finished.name]["value"] == 1
@ -571,7 +575,7 @@ class DelayGeneratorDG645(Device):
def trigger(self) -> DeviceStatus:
# if self.scaninfo.scan_type == "step":
if self.source.read()[self.source.name]["value"] == int(TriggerSource.SINGLE_SHOT):
self.trigger_shot.set(1).wait()
self.trigger_shot.put(1)
# status = super().trigger(status=)
status = DeviceStatus(self)
burst_state = threading.Thread(target=self._check_burst_cycle, args=(status,), daemon=True)
@ -590,19 +594,19 @@ class DelayGeneratorDG645(Device):
"first",
], "Supported bust configs are 'all' and 'first'"
self.burstMode.set(1).wait()
self.burstCount.set(count).wait()
self.burstDelay.set(delay).wait()
self.burstPeriod.set(period).wait()
self.burstMode.put(1)
self.burstCount.put(count)
self.burstDelay.put(delay)
self.burstPeriod.put(period)
if config == "all":
self.burstConfig.set(0).wait()
self.burstConfig.put(0)
elif config == "first":
self.burstConfig.set(1).wait()
self.burstConfig.put(1)
def burst_disable(self):
"""Disable the burst mode"""
self.burstMode.set(0).wait()
self.burstMode.put(0)
# Automatically connect to test environmenr if directly invoked

View File

@ -1,6 +1,9 @@
import os
from bec_lib.core import DeviceManagerBase, BECMessage, MessageEndpoints
from bec_lib.core import bec_logger
logger = bec_logger.logger
class BecScaninfoMixin:
@ -39,23 +42,28 @@ class BecScaninfoMixin:
info=self.bec_info_msg,
)
def _get_username(self) -> str:
def get_username(self) -> str:
if not self.sim_mode:
return self.device_manager.producer.get(MessageEndpoints.account()).decode()
return os.getlogin()
def load_scan_metadata(self) -> None:
self.scan_msg = scan_msg = self._get_current_scan_msg()
self.metadata = {
"scanID": scan_msg.content["scanID"],
"RID": scan_msg.content["info"]["RID"],
"queueID": scan_msg.content["info"]["queueID"],
}
self.scanID = scan_msg.content["scanID"]
self.scan_number = scan_msg.content["info"]["scan_number"]
self.exp_time = scan_msg.content["info"]["exp_time"]
self.frames_per_trigger = scan_msg.content["info"]["frames_per_trigger"]
self.num_points = scan_msg.content["info"]["num_points"]
self.scan_type = scan_msg.content["info"].get("scan_type", "step")
self.readout_time = scan_msg.content["info"]["readout_time"]
self.username = self._get_username()
logger.info(f"{self.scan_msg}")
try:
self.metadata = {
"scanID": scan_msg.content["scanID"],
"RID": scan_msg.content["info"]["RID"],
"queueID": scan_msg.content["info"]["queueID"],
}
self.scanID = scan_msg.content["scanID"]
self.scan_number = scan_msg.content["info"]["scan_number"]
self.exp_time = scan_msg.content["info"]["exp_time"]
self.frames_per_trigger = scan_msg.content["info"]["frames_per_trigger"]
self.num_points = scan_msg.content["info"]["num_points"]
self.scan_type = scan_msg.content["info"].get("scan_type", "step")
self.readout_time = scan_msg.content["info"]["readout_time"]
except Exception as exc:
logger.error(f"Failed to load scan metadata: {exc}.")
self.username = self.get_username()

View File

@ -4,9 +4,8 @@ from typing import Any, List
import numpy as np
from ophyd import EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
from ophyd import CamBase, DetectorBase, Device
from ophyd import DetectorBase, Device
from ophyd import ADComponent as ADCpt
from ophyd.areadetector.plugins import FileBase
from bec_lib.core import BECMessage, MessageEndpoints
from bec_lib.core.file_utils import FileWriterMixin
@ -25,7 +24,7 @@ class EigerError(Exception):
pass
class SlsDetectorCam(Device): # CamBase, FileBase):
class SlsDetectorCam(Device):
detector_type = ADCpt(EpicsSignalRO, "DetectorType_RBV")
setting = ADCpt(EpicsSignalWithRBV, "Setting")
delay_time = ADCpt(EpicsSignalWithRBV, "DelayTime")
@ -125,6 +124,7 @@ class Eiger9mCsaxs(DetectorBase):
parent=parent,
**kwargs,
)
self._stopped = False
if device_manager is None and not sim_mode:
raise EigerError("Add DeviceManager to initialization or init with sim_mode=True")
@ -234,16 +234,22 @@ class Eiger9mCsaxs(DetectorBase):
def _prep_file_writer(self) -> None:
self.filepath = self.filewriter.compile_full_filename(
self.scaninfo.scan_number, "eiger.h5", 1000, 5, True
self.scaninfo.scan_number, f"{self.name}.h5", 1000, 5, True
)
# self._close_file_writer()
logger.info(f" std_daq output filepath {self.filepath}")
self.std_client.start_writer_async(
{
"output_file": self.filepath,
"n_images": int(self.scaninfo.num_points * self.scaninfo.frames_per_trigger),
}
)
try:
self.std_client.start_writer_async(
{
"output_file": self.filepath,
"n_images": int(self.scaninfo.num_points * self.scaninfo.frames_per_trigger),
}
)
except Exception as exc:
time.sleep(5)
if self.std_client.get_status()["state"] == "READY":
raise EigerError(f"Timeout of start_writer_async with {exc}")
while True:
det_ctrl = self.std_client.get_status()["acquisition"]["state"]
if det_ctrl == "WAITING_IMAGES":
@ -277,24 +283,29 @@ class Eiger9mCsaxs(DetectorBase):
msg.dumps(),
)
self.arm_acquisition()
logger.info("Waiting for detector to be armed")
logger.info("Waiting for Eiger9m to be armed")
while True:
det_ctrl = self.cam.detector_state.read()[self.cam.detector_state.name]["value"]
if det_ctrl == int(DetectorState.RUNNING):
break
if self._stopped == True:
break
time.sleep(0.005)
logger.info("Detector is armed")
logger.info("Eiger9m is armed")
self._stopped = False
return super().stage()
def unstage(self) -> List[object]:
"""unstage the detector and file writer"""
logger.info("Waiting for eiger9M to return from acquisition")
logger.info("Waiting for Eiger9M to return from acquisition")
while True:
det_ctrl = self.cam.acquire.read()[self.cam.acquire.name]["value"]
if det_ctrl == 0:
break
if self._stopped == True:
break
time.sleep(0.005)
logger.info("Eiger9M finished")
logger.info("Waiting for std daq to receive images")
while True:
@ -302,7 +313,10 @@ class Eiger9mCsaxs(DetectorBase):
# TODO if no writing was performed before
if det_ctrl == "FINISHED":
break
if self._stopped == True:
break
time.sleep(0.005)
logger.info("Std_daq finished")
# Message to BEC
state = True
@ -311,7 +325,7 @@ class Eiger9mCsaxs(DetectorBase):
MessageEndpoints.public_file(self.scaninfo.scanID, self.name),
msg.dumps(),
)
logger.info("Eiger done")
self._stopped = False
return super().unstage()
def arm_acquisition(self) -> None:
@ -324,12 +338,9 @@ class Eiger9mCsaxs(DetectorBase):
"""Stop the scan, with camera and file writer"""
self.cam.acquire.set(0)
self._close_file_writer()
self.unstage()
super().stop(success=success)
self._stopped = True
# Automatically connect to test environmenr if directly invoked
if __name__ == "__main__":
eiger = Eiger9mCsaxs(name="eiger", prefix="X12SA-ES-EIGER9M:", sim_mode=True)
eiger.stage()

View File

@ -40,5 +40,6 @@ class EpicsMotorEx(EpicsMotor):
# set configuration attributes
for key, value in attrs.items():
# print out attributes that are being configured
print("setting ", key, "=", value)
getattr(self, key).put(value)

View File

@ -1,13 +1,14 @@
import enum
import os
import time
from typing import List
from ophyd import EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV, Component as Cpt, Device
from ophyd.mca import EpicsMCARecord, EpicsDXPMapping, EpicsDXPLowLevel, EpicsDXPMultiElementSystem
from ophyd.areadetector.plugins import HDF5Plugin, HDF5Plugin_V21, FilePlugin_V22
from ophyd.mca import EpicsMCARecord
from ophyd.areadetector.plugins import HDF5Plugin_V21, FilePlugin_V22
from bec_lib.core.file_utils import FileWriterMixin
from bec_lib.core import MessageEndpoints, BECMessage, RedisConnector
from bec_lib.core import MessageEndpoints, BECMessage
from bec_lib.core import bec_logger
from ophyd_devices.epics.devices.bec_scaninfo_mixin import BecScaninfoMixin
@ -16,6 +17,15 @@ from ophyd_devices.utils import bec_utils
logger = bec_logger.logger
class FalconError(Exception):
pass
class DetectorState(int, enum.Enum):
DONE = 0
ACQUIRING = 1
class EpicsDXPFalcon(Device):
"""All high-level DXP parameters for each channel"""
@ -37,12 +47,21 @@ class EpicsDXPFalcon(Device):
current_pixel = Cpt(EpicsSignalRO, "CurrentPixel")
class FalconError(Exception):
pass
class FalconHDF5Plugins(HDF5Plugin_V21, FilePlugin_V22):
pass
class FalconHDF5Plugins(Device): # HDF5Plugin_V21, FilePlugin_V22):
capture = Cpt(EpicsSignalWithRBV, "Capture")
enable = Cpt(EpicsSignalWithRBV, "EnableCallbacks", string=True, kind="config")
xml_file_name = Cpt(EpicsSignalWithRBV, "XMLFileName", string=True, kind="config")
lazy_open = Cpt(EpicsSignalWithRBV, "LazyOpen", string=True, doc="0='No' 1='Yes'")
temp_suffix = Cpt(EpicsSignalWithRBV, "TempSuffix", string=True)
# file_path = Cpt(
# EpicsSignalWithRBV, "FilePath", string=True, kind="config", path_semantics="posix"
# )
file_path = Cpt(EpicsSignalWithRBV, "FilePath", string=True, kind="config")
file_name = Cpt(EpicsSignalWithRBV, "FileName", string=True, kind="config")
file_template = Cpt(EpicsSignalWithRBV, "FileTemplate", string=True, kind="config")
num_capture = Cpt(EpicsSignalWithRBV, "NumCapture", kind="config")
file_write_mode = Cpt(EpicsSignalWithRBV, "FileWriteMode", kind="config")
capture = Cpt(EpicsSignalWithRBV, "Capture")
class FalconCsaxs(Device):
@ -77,6 +96,8 @@ class FalconCsaxs(Device):
pixels_per_buffer = Cpt(EpicsSignal, "PixelsPerBuffer")
pixels_per_run = Cpt(EpicsSignal, "PixelsPerRun")
# HDF5
def __init__(
self,
prefix="",
@ -101,153 +122,141 @@ class FalconCsaxs(Device):
)
if device_manager is None and not sim_mode:
raise FalconError("Add DeviceManager to initialization or init with sim_mode=True")
self._stopped = False
self.name = name
self.wait_for_connection() # Make sure to be connected before talking to PVs
if not sim_mode:
from bec_lib.core.bec_service import SERVICE_CONFIG
self.device_manager = device_manager
self._producer = self.device_manager.producer
self.service_cfg = SERVICE_CONFIG.config["service_config"]["file_writer"]
else:
self._producer = bec_utils.MockProducer()
self.device_manager = bec_utils.MockDeviceManager()
self.scaninfo = BecScaninfoMixin(device_manager, sim_mode)
self.scaninfo.load_scan_metadata()
self.service_cfg = {"base_path": f"/sls/X12SA/data/{self.scaninfo.username}/Data10/"}
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.scaninfo.load_scan_metadata()
self.filewriter = FileWriterMixin(self.service_cfg)
self.readout = 0.003 # 3 ms
self._value_pixel_per_buffer = 16
# TODO create file template from filewriter compile filename
self._file_template = f"%s%s_{self.name}.h5"
self.num_frames = 0
self._value_pixel_per_buffer = 1 # 16
self._clean_up()
self._init_hdf5_saving()
self._init_mapping_mode()
def _clean_up(self) -> None:
"""Clean up"""
self.hdf5.capture.set(0)
self.stop_all.set(1)
self.erase_all.set(1)
self.hdf5.capture.put(0)
self.stop_all.put(1)
self.erase_all.put(1)
def _init_hdf5_saving(self) -> None:
"""Set up hdf5 save parameters"""
self.hdf5.enable.set(1) # EnableCallbacks
self.hdf5.xml_file_name.set("layout.xml") # Points to hardcopy of HDF5 Layout xml file
self.hdf5.lazy_open.set(1) # Yes -> To be checked how to add FilePlugin_V21+
self.hdf5.temp_suffix.set("temps") # -> To be checked how to add FilePlugin_V22+
self.hdf5.enable.put(1) # EnableCallbacks
self.hdf5.xml_file_name.put("layout.xml") # Points to hardcopy of HDF5 Layout xml file
self.hdf5.lazy_open.put(1) # Yes -> To be checked how to add FilePlugin_V21+
self.hdf5.temp_suffix.put("temps") # -> To be checked how to add FilePlugin_V22+
def _init_mapping_mode(self) -> None:
"""Set up mapping mode params"""
self.collect_mode.set(1) # 1 MCA Mapping, 0 MCA Spectrum
self.preset_mode.set(1) # 1 Realtime
self.input_logic_polarity.set(0) # 0 Normal, 1 Inverted
self.pixel_advance_mode.set(1) # 0 User, 1 Gate, 2 Sync
self.ignore_gate.set(1) # 1 Yes
self.auto_pixels_per_buffer.set(0) # 0 Manual 1 Auto
self.pixels_per_buffer.set(16) #
def _get_current_scan_msg(self) -> BECMessage.ScanStatusMessage:
msg = self.device_manager.producer.get(MessageEndpoints.scan_status())
return BECMessage.ScanStatusMessage.loads(msg)
def _load_scan_metadata(self) -> None:
scan_msg = self._get_current_scan_msg()
self.metadata = {
"scanID": scan_msg.content["scanID"],
"RID": scan_msg.content["info"]["RID"],
"queueID": scan_msg.content["info"]["queueID"],
}
self.scanID = scan_msg.content["scanID"]
self.scan_number = scan_msg.content["info"]["scan_number"]
self.exp_time = scan_msg.content["info"]["exp_time"]
self.num_frames = scan_msg.content["info"]["num_points"]
self.username = self.device_manager.producer.get(MessageEndpoints.account()).decode()
self.device_manager.devices.mokev.read()["mokev"]["value"]
# self.triggermode = scan_msg.content["info"]["trigger_mode"]
self.filepath = self.filewriter.compile_full_filename(
self.scan_number, "falcon", 1000, 5, True
)
self.collect_mode.put(1) # 1 MCA Mapping, 0 MCA Spectrum
self.preset_mode.put(1) # 1 Realtime
self.input_logic_polarity.put(0) # 0 Normal, 1 Inverted
self.pixel_advance_mode.put(1) # 0 User, 1 Gate, 2 Sync
self.ignore_gate.put(1) # 1 Yes
self.auto_pixels_per_buffer.put(0) # 0 Manual 1 Auto
self.pixels_per_buffer.put(16) #
def _prep_det(self) -> None:
"""Prepare detector for acquisition"""
self.collect_mode.set(1)
self.preset_real.set(self.exposure_time)
self.pixels_per_run.set(self.num_frames)
self.auto_pixels_per_buffer.set(0)
self.pixels_per_buffer.set(self._value_pixel_per_buffer)
self.collect_mode.put(1)
self.preset_real.put(self.scaninfo.exp_time)
self.pixels_per_run.put(int(self.scaninfo.num_points * self.scaninfo.frames_per_trigger))
self.auto_pixels_per_buffer.put(0)
self.pixels_per_buffer.put(self._value_pixel_per_buffer)
def _prep_file_writer(self) -> None:
"""Prep HDF5 weriting"""
# TODO creta filename and destination path from filepath
self.destination_path = os.path.join(self.service_cfg["base_path"])
self.filename = f"test_{self.scan_number}"
self.hdf5.file_path.set(self.destination_path)
self.hdf5.file_name.set(self.filename)
self.hdf5.file_template.set(self._file_template)
self.hdf5.num_capture.set(self.num_frames // self._value_pixel_per_buffer + 1)
self.hdf5.file_write_mode.set(2)
self.hdf5.capture.set(1)
self.destination_path = self.filewriter.compile_full_filename(
self.scaninfo.scan_number, f"{self.name}.h5", 1000, 5, True
)
# self.hdf5.file_path.set(self.destination_path)
file_path, file_name = os.path.split(self.destination_path)
self.hdf5.file_path.put(file_path)
self.hdf5.file_name.put(file_name)
self.hdf5.file_template.put(f"%s%s")
self.hdf5.num_capture.put(self.scaninfo.num_points // self._value_pixel_per_buffer + 1)
self.hdf5.file_write_mode.put(2)
self.hdf5.capture.put(1)
def stage(self) -> List[object]:
"""stage the detector and file writer"""
# TODO remove once running from BEC
# self._load_scan_metadata()
self.scan_number = 10
self.exp_time = 0.5
self.num_frames = 3
self.mokev = 12
# TODO clean up needed?
# self._clean_up()
self.scaninfo.load_scan_metadata()
self.mokev = self.device_manager.devices.mokev.obj.read()[
self.device_manager.devices.mokev.name
]["value"]
logger.info("Waiting for pilatus2 to be armed")
self._prep_det()
logger.info("Pilatus2 armed")
logger.info("Waiting for pilatus2 zmq stream to be ready")
self._prep_file_writer()
logger.info("Pilatus2 zmq ready")
msg = BECMessage.FileMessage(file_path=self.filepath, done=False)
self.producer.set_and_publish(
MessageEndpoints.public_file(self.metadata["scanID"], self.name),
msg = BECMessage.FileMessage(file_path=self.destination_path, done=False)
self._producer.set_and_publish(
MessageEndpoints.public_file(self.scaninfo.scanID, self.name),
msg.dumps(),
)
self.arm_acquisition()
logger.info("Waiting for Falcon to be armed")
while True:
det_ctrl = self.state.read()[self.state.name]["value"]
if det_ctrl == int(DetectorState.ACQUIRING):
break
if self._stopped == True:
break
time.sleep(0.005)
logger.info("Falcon is armed")
self._stopped = False
return super().stage()
def acquire(self) -> None:
self.start_all.set(1)
def arm_acquisition(self) -> None:
self.start_all.put(1)
def unstage(self) -> List[object]:
logger.info("Waiting for Falcon to return from acquisition")
while True:
det_ctrl = self.state.read()[self.state.name]["value"]
if det_ctrl == int(DetectorState.DONE):
break
if self._stopped == True:
break
time.sleep(0.005)
logger.info("Falcon done")
# TODO needed?
self._clean_up()
# TODO check if acquisition is done and successful!
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 = BECMessage.FileMessage(file_path=self.destination_path, done=True, successful=state)
self._producer.set_and_publish(
MessageEndpoints.public_file(self.scaninfo.metadata["scanID"], self.name),
msg.dumps(),
)
self._stopped = False
return super().unstage()
def _check_falcon_done(self) -> bool:
state = self.state.read()[f"{self.name }_state"]["value"]
if state is [0, 1]:
return not bool(state)
else:
# 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()
"""Stop the scan, with camera and file writer"""
self._clean_up()
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()

View File

@ -93,6 +93,9 @@ class SIS38XX(Device):
class McsCsaxs(SIS38XX):
SUB_PROGRESS = "progress"
SUB_VALUE = "value"
_default_sub = SUB_VALUE
# scaler = Cpt(ScalerCH, "scaler1")
# mca2 = Cpt(EpicsMCARecord, "mca2")
@ -127,6 +130,7 @@ class McsCsaxs(SIS38XX):
# mca30 = Cpt(EpicsMCARecord, "mca30")
# mca31 = Cpt(EpicsMCARecord, "mca31")
# mca32 = Cpt(EpicsMCARecord, "mca32")
current_channel = Cpt(EpicsSignalRO, "CurrentChannel", auto_monitor=True)
num_lines = Cpt(
bec_utils.ConfigSignal,
@ -164,7 +168,6 @@ class McsCsaxs(SIS38XX):
parent=parent,
**kwargs,
)
if device_manager is None and not sim_mode:
raise MCSError("Add DeviceManager to initialization or init with sim_mode=True")
@ -188,6 +191,8 @@ class McsCsaxs(SIS38XX):
self._stopped = False
self._acquisition_done = False
self._lock = threading.RLock()
self.counter = 0
self.n_points = 0
self._init_mcs()
def _init_mcs(self) -> None:
@ -212,7 +217,17 @@ class McsCsaxs(SIS38XX):
for mca in self.mca_names:
signal = getattr(self, mca)
signal.subscribe(self._on_mca_data, run=False)
self._counter = 0
self.current_channel.subscribe(self._progress_update, run=False)
def _progress_update(self, value, **kwargs) -> None:
num_lines = self.num_lines.get()
max_value = self.scaninfo.num_points
self._run_subs(
sub_type=self.SUB_PROGRESS,
value=self.counter * int(self.scaninfo.num_points / num_lines) + max(value - 1, 0),
max_value=max_value,
done=bool(max_value == self.counter),
)
@threadlocked
def _on_mca_data(self, *args, obj=None, **kwargs) -> None:
@ -229,19 +244,18 @@ class McsCsaxs(SIS38XX):
# return
self._updated = True
self._counter += 1
logger.info(f"counter {self._counter}")
if (self.scaninfo.scan_type == "fly" and self._counter == self.num_lines.get()) or (
self.scaninfo.scan_type == "step" and self._counter == self.scaninfo.num_points
self.counter += 1
if (self.scaninfo.scan_type == "fly" and self.counter == self.num_lines.get()) or (
self.scaninfo.scan_type == "step" and self.counter == self.scaninfo.num_points
):
self._acquisition_done = True
self.stop_all.put(1, use_complete=False)
self._send_data_to_bec()
self.erase_all.set(1)
self.erase_all.put(1)
# Require wait for
# time.sleep(0.01)
self.mca_data = defaultdict(lambda: [])
self._counter = 0
self.counter = 0
return
self.erase_start.set(1)
self._send_data_to_bec()
@ -257,7 +271,6 @@ class McsCsaxs(SIS38XX):
"num_lines": self.num_lines.get(),
}
)
logger.info(f"{self.mca_data}")
msg = BECMessage.DeviceMessage(
signals=dict(self.mca_data),
metadata=self.scaninfo.scan_msg.metadata,
@ -276,16 +289,16 @@ class McsCsaxs(SIS38XX):
def _set_acquisition_params(self) -> None:
if self.scaninfo.scan_type == "step":
n_points = int(self.scaninfo.frames_per_trigger + 1)
self.n_points = int(self.scaninfo.frames_per_trigger + 1)
elif self.scaninfo.scan_type == "fly":
n_points = int(self.scaninfo.num_points / int(self.num_lines.get()) + 1)
self.n_points = int(self.scaninfo.num_points / int(self.num_lines.get()) + 1)
else:
raise MCSError(f"Scantype {self.scaninfo} not implemented for MCS card")
if n_points > 10000:
if self.n_points > 10000:
raise MCSError(
f"Requested number of points N={n_points} exceeds hardware limit of mcs card 10000 (N-1)"
f"Requested number of points N={self.n_points} exceeds hardware limit of mcs card 10000 (N-1)"
)
self.num_use_all.set(n_points)
self.num_use_all.set(self.n_points)
self.preset_real.set(0)
def _set_trigger(self, trigger_source: TriggerSource) -> None:
@ -299,7 +312,7 @@ class McsCsaxs(SIS38XX):
Check ReadoutMode class for more information about options
"""
# self.read_mode.set(ReadoutMode.EVENT)
self.erase_all.set(1)
self.erase_all.put(1)
self.read_mode.set(ReadoutMode.EVENT)
def _force_readout_mcs_card(self) -> None:
@ -307,7 +320,7 @@ class McsCsaxs(SIS38XX):
def stage(self) -> List[object]:
"""stage the detector and file writer"""
logger.info("Stage Eiger")
logger.info("Stage mcs")
self.scaninfo.load_scan_metadata()
self._prep_det()
self._prep_readout()
@ -347,7 +360,7 @@ class McsCsaxs(SIS38XX):
Start: start_all
Erase/Start: erase_start
"""
self._counter = 0
self.counter = 0
self.erase_start.set(1)
# self.start_all.set(1)
@ -359,7 +372,7 @@ class McsCsaxs(SIS38XX):
# self.erase_all.set(1)
self._stopped = True
self._acquisition_done = True
self._counter = 0
self.counter = 0
super().stop(success=success)

View File

@ -6,11 +6,12 @@ from typing import List
import requests
import numpy as np
from ophyd.areadetector import ADComponent as ADCpt, PilatusDetectorCam, DetectorBase
from ophyd.areadetector.plugins import FileBase
from ophyd import EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
from ophyd import DetectorBase, Device
from ophyd import ADComponent as ADCpt
from ophyd_devices.utils import bec_utils as bec_utils
from bec_lib.core import BECMessage, MessageEndpoints, RedisConnector
from bec_lib.core import BECMessage, MessageEndpoints
from bec_lib.core.file_utils import FileWriterMixin
from bec_lib.core import bec_logger
@ -24,10 +25,6 @@ class PilatusError(Exception):
pass
class PilatusDetectorCamEx(PilatusDetectorCam, FileBase):
pass
class TriggerSource(int, enum.Enum):
INTERNAL = 0
EXT_ENABLE = 1
@ -36,6 +33,72 @@ class TriggerSource(int, enum.Enum):
ALGINMENT = 4
class SlsDetectorCam(Device): # CamBase, FileBase):
# detector_type = ADCpt(EpicsSignalRO, "DetectorType_RBV")
# setting = ADCpt(EpicsSignalWithRBV, "Setting")
# beam_energy = ADCpt(EpicsSignalWithRBV, "BeamEnergy")
# enable_trimbits = ADCpt(EpicsSignalWithRBV, "Trimbits")
# bit_depth = ADCpt(EpicsSignalWithRBV, "BitDepth")
# trigger_software = ADCpt(EpicsSignal, "TriggerSoftware")
# high_voltage = ADCpt(EpicsSignalWithRBV, "HighVoltage")
# Receiver and data callback
# receiver_mode = ADCpt(EpicsSignalWithRBV, "ReceiverMode")
# receiver_stream = ADCpt(EpicsSignalWithRBV, "ReceiverStream")
# enable_data = ADCpt(EpicsSignalWithRBV, "UseDataCallback")
# missed_packets = ADCpt(EpicsSignalRO, "ReceiverMissedPackets_RBV")
# # Direct settings access
# setup_file = ADCpt(EpicsSignal, "SetupFile")
# load_setup = ADCpt(EpicsSignal, "LoadSetup")
# command = ADCpt(EpicsSignal, "Command")
# Mythen 3
# counter_mask = ADCpt(EpicsSignalWithRBV, "CounterMask")
# counter1_threshold = ADCpt(EpicsSignalWithRBV, "Counter1Threshold")
# counter2_threshold = ADCpt(EpicsSignalWithRBV, "Counter2Threshold")
# counter3_threshold = ADCpt(EpicsSignalWithRBV, "Counter3Threshold")
# gate1_delay = ADCpt(EpicsSignalWithRBV, "Gate1Delay")
# gate1_width = ADCpt(EpicsSignalWithRBV, "Gate1Width")
# gate2_delay = ADCpt(EpicsSignalWithRBV, "Gate2Delay")
# gate2_width = ADCpt(EpicsSignalWithRBV, "Gate2Width")
# gate3_delay = ADCpt(EpicsSignalWithRBV, "Gate3Delay")
# gate3_width = ADCpt(EpicsSignalWithRBV, "Gate3Width")
# Moench
# json_frame_mode = ADCpt(EpicsSignalWithRBV, "JsonFrameMode")
# json_detector_mode = ADCpt(EpicsSignalWithRBV, "JsonDetectorMode")
# Eiger9M
# delay_time = ADCpt(EpicsSignalWithRBV, "DelayTime")
# num_frames = ADCpt(EpicsSignalWithRBV, "NumFrames")
# acquire = ADCpt(EpicsSignal, "Acquire")
# acquire_time = ADCpt(EpicsSignal, 'AcquireTime')
# detector_state = ADCpt(EpicsSignalRO, "DetectorState_RBV")
# threshold_energy = ADCpt(EpicsSignalWithRBV, "ThresholdEnergy")
# num_gates = ADCpt(EpicsSignalWithRBV, "NumGates")
# num_cycles = ADCpt(EpicsSignalWithRBV, "NumCycles")
# timing_mode = ADCpt(EpicsSignalWithRBV, "TimingMode")
# Pilatus_2 300k
num_images = ADCpt(EpicsSignalWithRBV, "NumImages")
num_exposures = ADCpt(EpicsSignalWithRBV, "NumExposures")
delay_time = ADCpt(EpicsSignalWithRBV, "NumExposures")
trigger_mode = ADCpt(EpicsSignalWithRBV, "TriggerMode")
acquire = ADCpt(EpicsSignal, "Acquire")
armed = ADCpt(EpicsSignalRO, "Armed")
read_file_timeout = ADCpt(EpicsSignal, "ImageFileTmot")
detector_state = ADCpt(EpicsSignalRO, "StatusMessage_RBV")
status_message_camserver = ADCpt(EpicsSignalRO, "StringFromServer_RBV")
acquire_time = ADCpt(EpicsSignal, "AcquireTime")
acquire_period = ADCpt(EpicsSignal, "AcquirePeriod")
threshold_energy = ADCpt(EpicsSignalWithRBV, "ThresholdEnergy")
file_path = ADCpt(EpicsSignalWithRBV, "FilePath")
file_name = ADCpt(EpicsSignalWithRBV, "FileName")
file_number = ADCpt(EpicsSignalWithRBV, "FileNumber")
auto_increment = ADCpt(EpicsSignalWithRBV, "AutoIncrement")
file_template = ADCpt(EpicsSignalWithRBV, "FileTemplate")
file_format = ADCpt(EpicsSignalWithRBV, "FileNumber")
gap_fill = ADCpt(EpicsSignalWithRBV, "GapFill")
class PilatusCsaxs(DetectorBase):
"""Pilatus_2 300k detector for CSAXS
@ -43,12 +106,12 @@ class PilatusCsaxs(DetectorBase):
Device class: PilatusDetectorCamEx
Attributes:
name str: 'eiger'
name str: 'pilatus_2'
prefix (str): PV prefix (X12SA-ES-PILATUS300K:)
"""
cam = ADCpt(PilatusDetectorCamEx, "cam1:")
cam = ADCpt(SlsDetectorCam, "cam1:")
def __init__(
self,
@ -91,7 +154,7 @@ class PilatusCsaxs(DetectorBase):
self.service_cfg = {"base_path": f"/sls/X12SA/data/{self.scaninfo.username}/Data10/"}
self.scaninfo = BecScaninfoMixin(device_manager, sim_mode)
self.filepath = ""
self.filepath_h5 = ""
self.filewriter = FileWriterMixin(self.service_cfg)
self.readout = 1e-3 # 3 ms
@ -100,28 +163,6 @@ class PilatusCsaxs(DetectorBase):
msg = self.device_manager.producer.get(MessageEndpoints.scan_status())
return BECMessage.ScanStatusMessage.loads(msg)
# def _load_scan_metadata(self) -> None:
# scan_msg = self._get_current_scan_msg()
# self.metadata = {
# "scanID": scan_msg.content["scanID"],
# "RID": scan_msg.content["info"]["RID"],
# "queueID": scan_msg.content["info"]["queueID"],
# }
# self.scanID = scan_msg.content["scanID"]
# self.scan_number = scan_msg.content["info"]["scan_number"]
# self.exp_time = scan_msg.content["info"]["exp_time"]
# self.num_frames = scan_msg.content["info"]["num_points"]
# self.username = self.device_manager.producer.get(
# MessageEndpoints.account()
# ).decode()
# self.device_manager.devices.mokev.read()["mokev"]["value"]
# # self.triggermode = scan_msg.content["info"]["trigger_mode"]
# # self.filename = self.filewriter.compile_full_filename(
# # self.scan_number, "pilatus_2", 1000, 5, True
# # )
# # TODO fix with BEC running
# # self.filename = '/sls/X12SA/Data10/e21206/data/test.h5'
def _prep_det(self) -> None:
# TODO slow reaction, seemed to have timeout.
self._set_det_threshold()
@ -143,7 +184,7 @@ class PilatusCsaxs(DetectorBase):
# self.cam.acquire_period.set(self.exp_time + self.readout)
self.cam.num_images.set(int(self.scaninfo.num_points * self.scaninfo.frames_per_trigger))
self.cam.num_exposures.set(1)
self._set_trigger(TriggerSource.INTERNAL) # EXT_TRIGGER)
self._set_trigger(TriggerSource.EXT_ENABLE) # EXT_TRIGGER)
def _set_trigger(self, trigger_source: TriggerSource) -> None:
"""Set trigger source for the detector, either directly to value or TriggerSource.* with
@ -164,12 +205,12 @@ class PilatusCsaxs(DetectorBase):
self.filepath_h5 = self.filewriter.compile_full_filename(
self.scaninfo.scan_number, "pilatus_2.h5", 1000, 5, True
)
self.cam.file_path.set(f"/dev/shm/zmq/")
self.cam.file_name.set(f"{self.scaninfo.username}_2_{self.scaninfo.scan_number:05d}")
self.cam.auto_increment.set(1) # auto increment
self.cam.file_number.set(0) # first iter
self.cam.file_format.set(0) # 0: TIFF
self.cam.file_template.set("%s%s_%5.5d.cbf")
self.cam.file_path.put(f"/dev/shm/zmq/")
self.cam.file_name.put(f"{self.scaninfo.username}_2_{self.scaninfo.scan_number:05d}")
self.cam.auto_increment.put(1) # auto increment
self.cam.file_number.put(0) # first iter
self.cam.file_format.put(0) # 0: TIFF
self.cam.file_template.put("%s%s_%5.5d.cbf")
# compile filename
basepath = f"/sls/X12SA/data/{self.scaninfo.username}/Data10/pilatus_2/"
@ -254,17 +295,19 @@ class PilatusCsaxs(DetectorBase):
if not res.ok:
res.raise_for_status()
except Exception as exc:
logger.info("exc")
logger.info(f"Pilatus2 wait threw Exception: {exc}")
def _close_file_writer(self) -> None:
"""Close the file writer for pilatus_2
a zmq service is running on xbl-daq-34 that is waiting
for a zmq message to stop the writer for the pilatus_2 x12sa-pd-2
"""
res = requests.delete(url="http://x12sa-pd-2:8080/stream/pilatus_2")
if not res.ok:
res.raise_for_status()
try:
res = requests.delete(url="http://x12sa-pd-2:8080/stream/pilatus_2")
if not res.ok:
res.raise_for_status()
except Exception as exc:
logger.info(f"Pilatus2 delete threw Exception: {exc}")
def _stop_file_writer(self) -> None:
res = requests.put(
@ -278,34 +321,48 @@ class PilatusCsaxs(DetectorBase):
def stage(self) -> List[object]:
"""stage the detector and file writer"""
self._close_file_writer()
self._stop_file_writer()
self.scaninfo.load_scan_metadata()
self.mokev = self.device_manager.devices.mokev.obj.read()[
self.device_manager.devices.mokev.name
]["value"]
logger.info("Waiting for pilatus2 to be armed")
self._prep_det()
logger.info("Pilatus2 armed")
logger.info("Waiting for pilatus2 zmq stream to be ready")
self._prep_file_writer()
self.acquire()
logger.info("Pilatus2 zmq ready")
msg = BECMessage.FileMessage(
file_path=self.filepath_h5, done=False, metadata={"input_path": self.destination_path}
)
return super().stage()
def pre_scan(self) -> None:
self.acquire()
def unstage(self) -> List[object]:
"""unstage the detector and file writer"""
# Reset to software trigger
self.triggermode = 0
# TODO if images are missing, consider adding delay
self._close_file_writer()
self._stop_file_writer()
# Only sent this out once data is written to disk since cbf to hdf5 converter will be triggered
msg = BECMessage.FileMessage(file_path=self.filepath, done=True)
msg = BECMessage.FileMessage(
file_path=self.filepath_h5, done=True, metadata={"input_path": self.destination_path}
)
self._producer.set_and_publish(
MessageEndpoints.public_file(self.scaninfo.scanID, self.name),
msg.dumps(),
)
msg = BECMessage.FileMessage(file_path=self.filepath, done=True)
self._producer.set_and_publish(
MessageEndpoints.file_event(self.name),
msg.dumps(),
)
logger.info("Pilatus2 done")
return super().unstage()
def acquire(self) -> None:

View File

@ -155,7 +155,8 @@ class GalilController(Controller):
def stop_all_axes(self) -> str:
# return self.socket_put_and_receive(f"XQ#STOP,1")
# Command stops all threads and motors!
return self.socket_put_and_receive(f"ST")
# self.socket_put_and_receive(f"ST")
return self.socket_put_and_receive(f"AB")
def axis_is_referenced(self) -> bool:
return bool(float(self.socket_put_and_receive(f"MG allaxref").strip()))
@ -405,7 +406,7 @@ class GalilSetpointSignal(GalilSignalBase):
Returns:
float: setpoint / target value
"""
return self.setpoint
return self.setpoint * self.parent.sign
@retry_once
@threadlocked