refactor: class refactoring, pending change to SlsDetectorCam

This commit is contained in:
appel_c 2023-08-21 21:52:09 +02:00
parent 287c667621
commit b1150c41fe

View File

@ -1,17 +1,17 @@
import json import json
import os import os
from typing import List
import requests import requests
import numpy as np import numpy as np
from typing import List
from ophyd.areadetector import ADComponent as ADCpt, PilatusDetectorCam, DetectorBase from ophyd.areadetector import ADComponent as ADCpt, PilatusDetectorCam, DetectorBase
from ophyd.areadetector.plugins import FileBase from ophyd.areadetector.plugins import FileBase
from bec_lib.core import BECMessage, MessageEndpoints from bec_lib.core import BECMessage, MessageEndpoints, RedisConnector
from bec_lib.core.file_utils import FileWriterMixin from bec_lib.core.file_utils import FileWriterMixin
from bec_lib.core import bec_logger from bec_lib.core import bec_logger
logger = bec_logger.logger logger = bec_logger.logger
@ -19,14 +19,18 @@ class PilatusDetectorCamEx(PilatusDetectorCam, FileBase):
pass pass
# TODO refactor class -> move away from DetectorBase and PilatusDetectorCamEx class to Device. -> this will be cleaner
class PilatusCsaxs(DetectorBase): class PilatusCsaxs(DetectorBase):
"""Pilatus_2 300k detector for CSAXS
Parent class: DetectorBase
Device class: PilatusDetectorCamEx
Attributes:
name str: 'eiger'
prefix (str): PV prefix (X12SA-ES-PILATUS300K:)
""" """
in device config, device_access needs to be set true to inject the device manager
"""
_html_docs = ["PilatusDoc.html"]
cam = ADCpt(PilatusDetectorCamEx, "cam1:") cam = ADCpt(PilatusDetectorCamEx, "cam1:")
def __init__( def __init__(
@ -41,9 +45,6 @@ class PilatusCsaxs(DetectorBase):
device_manager=None, device_manager=None,
**kwargs, **kwargs,
): ):
self.device_manager = device_manager
self.username = "e21206" # TODO get from config
# self.username = self.device_manager.producer.get(MessageEndpoints.account()).decode()
super().__init__( super().__init__(
prefix=prefix, prefix=prefix,
name=name, name=name,
@ -53,10 +54,15 @@ class PilatusCsaxs(DetectorBase):
parent=parent, parent=parent,
**kwargs, **kwargs,
) )
# TODO how to get base_path self.device_manager = device_manager
self.service_cfg = {"base_path": f"/sls/X12SA/data/{self.username}/Data10/pilatus_2/"} self.name = name
self.username = "e21206"
# TODO once running from BEC
# self.username = self.device_manager.producer.get(MessageEndpoints.account()).decode()
self.service_cfg = {"base_path": f"/sls/X12SA/data/{self.username}/Data10/data/"}
self.filewriter = FileWriterMixin(self.service_cfg) self.filewriter = FileWriterMixin(self.service_cfg)
self.num_frames = 0 self._producer = RedisConnector(["localhost:6379"]).producer()
self.readout = 0.003 # 3 ms self.readout = 0.003 # 3 ms
self.triggermode = 0 # 0 : internal, scan must set this if hardware triggered self.triggermode = 0 # 0 : internal, scan must set this if hardware triggered
@ -64,24 +70,46 @@ class PilatusCsaxs(DetectorBase):
msg = self.device_manager.producer.get(MessageEndpoints.scan_status()) msg = self.device_manager.producer.get(MessageEndpoints.scan_status())
return BECMessage.ScanStatusMessage.loads(msg) return BECMessage.ScanStatusMessage.loads(msg)
def stage(self) -> List[object]: def _load_scan_metadata(self) -> None:
# TODO remove scan_msg = self._get_current_scan_msg()
# scan_msg = self._get_current_scan_msg() self.metadata = {
# self.metadata = { "scanID": scan_msg.content["scanID"],
# "scanID": scan_msg.content["scanID"], "RID": scan_msg.content["info"]["RID"],
# "RID": scan_msg.content["info"]["RID"], "queueID": scan_msg.content["info"]["queueID"],
# "queueID": scan_msg.content["info"]["queueID"], }
# } self.scanID = scan_msg.content["scanID"]
self.scan_number = 10 # scan_msg.content["info"]["scan_number"] self.scan_number = scan_msg.content["info"]["scan_number"]
self.exp_time = 0.5 # scan_msg.content["info"]["exp_time"] self.exp_time = scan_msg.content["info"]["exp_time"]
self.num_frames = 3 # scan_msg.content["info"]["num_points"] self.num_frames = scan_msg.content["info"]["num_points"]
# TODO remove self.username = self.device_manager.producer.get(MessageEndpoints.account()).decode()
# 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
)
# set pilatus threshol def _prep_det(self) -> None:
self._set_threshold() self._set_det_threshold()
self._set_acquisition_params()
# set Epic PVs for filewriting def _set_det_threshold(self) -> None:
threshold = self.cam.threshold_energy.read()[self.cam.threshold_energy.name]["value"]
if not np.isclose(self.mokev / 2, threshold, rtol=0.05):
self.cam.threshold_energy.set(self.mokev / 2)
def _set_acquisition_params(self) -> None:
"""set acquisition parameters on the detector"""
self.cam.acquire_time.set(self.exp_time)
self.cam.acquire_period.set(self.exp_time + self.readout)
self.cam.num_images.set(self.num_frames)
self.cam.num_exposures.set(1)
self.cam.trigger_mode.set(self.triggermode)
def _prep_file_writer(self) -> None:
"""Prepare the file writer for pilatus_2
a zmq service is running on xbl-daq-34 that is waiting
for a zmq message to start the writer for the pilatus_2 x12sa-pd-2
"""
self.cam.file_path.set(f"/dev/shm/zmq/") self.cam.file_path.set(f"/dev/shm/zmq/")
self.cam.file_name.set(f"{self.username}_2_{self.scan_number:05d}") self.cam.file_name.set(f"{self.username}_2_{self.scan_number:05d}")
self.cam.auto_increment.set(1) # auto increment self.cam.auto_increment.set(1) # auto increment
@ -89,13 +117,12 @@ class PilatusCsaxs(DetectorBase):
self.cam.file_format.set(0) # 0: TIFF self.cam.file_format.set(0) # 0: TIFF
self.cam.file_template.set("%s%s_%5.5d.cbf") self.cam.file_template.set("%s%s_%5.5d.cbf")
# compile zmq stream for data transfer # TODO Filewriter Plugin to write cbfs to h5!
scan_dir = self.filewriter._get_scan_directory( # Pilatus_2 writes cbf files -> where do we like to write those!
scan_bundle=1000, scan_number=self.scan_number, leading_zeros=5 # scan_dir = self.filewriter._get_scan_directory(
) # scan_bundle=1000, scan_number=self.scan_number, leading_zeros=5
self.destination_path = os.path.join( # ) # os.path.join(self.service_cfg["base_path"], scan_dir)
self.service_cfg["base_path"] self.destination_path = "/sls/X12SA/data/{self.username}/Data10/pilatus_2/"
) # os.path.join(self.service_cfg["base_path"], scan_dir)
data_msg = { data_msg = {
"source": [ "source": [
@ -106,6 +133,7 @@ class PilatusCsaxs(DetectorBase):
} }
] ]
} }
logger.info(data_msg) logger.info(data_msg)
headers = {"Content-Type": "application/json", "Accept": "application/json"} headers = {"Content-Type": "application/json", "Accept": "application/json"}
@ -141,16 +169,11 @@ class PilatusCsaxs(DetectorBase):
if not res.ok: if not res.ok:
res.raise_for_status() res.raise_for_status()
self._set_acquisition_params( def _close_file_writer(self) -> None:
exp_time=self.exp_time, """Close the file writer for pilatus_2
readout=self.readout, a zmq service is running on xbl-daq-34 that is waiting
num_frames=self.num_frames, for a zmq message to stop the writer for the pilatus_2 x12sa-pd-2
triggermode=self.triggermode, """
)
return super().stage()
def unstage(self) -> List[object]:
headers = {"Content-Type": "application/json", "Accept": "application/json"} headers = {"Content-Type": "application/json", "Accept": "application/json"}
data_msg = [ data_msg = [
"zmqWriter", "zmqWriter",
@ -167,8 +190,6 @@ class PilatusCsaxs(DetectorBase):
data=json.dumps(data_msg), data=json.dumps(data_msg),
headers=headers, headers=headers,
) )
# Reset triggermode to internal
self.triggermode = 0
if not res.ok: if not res.ok:
res.raise_for_status() res.raise_for_status()
@ -176,46 +197,49 @@ class PilatusCsaxs(DetectorBase):
res = requests.delete(url="http://x12sa-pd-2:8080/stream/pilatus_2") res = requests.delete(url="http://x12sa-pd-2:8080/stream/pilatus_2")
if not res.ok: if not res.ok:
res.raise_for_status() res.raise_for_status()
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
self._prep_det()
self._prep_file_writer()
msg = BECMessage.FileMessage(file_path=self.filename, done=False)
self._producer.set_and_publish(
MessageEndpoints.public_file(self.scanID, "pilatus_2"),
msg.dumps(),
)
return super().stage()
def unstage(self) -> List[object]:
"""unstage the detector and file writer"""
# Reset to software trigger
self.triggermode = 0
self._close_file_writer()
# 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.dumps(),
)
return super().unstage() return super().unstage()
def _set_threshold(self) -> None:
# TODO readout mono, monitor threshold and set it if mokev is different
# mokev = self.device_manager.devices.mokev.obj.read()["mokev"]["value"]
# TODO remove
mokev = 16
# TODO refactor naming from name, pilatus_2
pil_threshold = self.cam.threshold_energy.read()["pilatus_2_cam_threshold_energy"]["value"]
if not np.isclose(mokev / 2, pil_threshold, rtol=0.05):
self.cam.threshold_energy.set(mokev / 2)
def _set_acquisition_params(
self, exp_time: float, readout: float, num_frames: int, triggermode: int
) -> None:
"""set acquisition parameters on the detector
Args:
exp_time (float): exposure time
readout (float): readout time
num_frames (int): images per scan
triggermode (int):
0 Internal
1 Ext. Enable
2 Ext. Trigger
3 Mult. Trigger
4 Alignment
Returns:
None
"""
self.cam.acquire_time.set(exp_time)
self.cam.acquire_period.set(exp_time + readout)
self.cam.num_images.set(num_frames)
self.cam.num_exposures.set(1)
self.cam.trigger_mode.set(triggermode)
def acquire(self) -> None: def acquire(self) -> None:
"""Start acquisition in software trigger mode,
or arm the detector in hardware of the detector
"""
self.cam.acquire.set(1) self.cam.acquire.set(1)
def stop(self, *, success=False) -> None: def stop(self, *, success=False) -> None:
"""Stop the scan, with camera and file writer"""
self.cam.acquire.set(0) self.cam.acquire.set(0)
self.unstage() self.unstage()
super().stop(success=success) super().stop(success=success)