Bump
This commit is contained in:
@@ -11,6 +11,19 @@ eyex:
|
|||||||
readOnly: false
|
readOnly: false
|
||||||
softwareTrigger: false
|
softwareTrigger: false
|
||||||
|
|
||||||
|
eyey:
|
||||||
|
readoutPriority: baseline
|
||||||
|
description: X-ray eye axis Y
|
||||||
|
deviceClass: tomcat_bec.devices.psimotor.EpicsMotorEC
|
||||||
|
deviceConfig:
|
||||||
|
prefix: MTEST-X05LA-ES2-XRAYEYE:M2
|
||||||
|
deviceTags:
|
||||||
|
- xray-eye
|
||||||
|
onFailure: buffer
|
||||||
|
enabled: true
|
||||||
|
readOnly: false
|
||||||
|
softwareTrigger: false
|
||||||
|
|
||||||
eyez:
|
eyez:
|
||||||
readoutPriority: baseline
|
readoutPriority: baseline
|
||||||
description: X-ray eye axis Z
|
description: X-ray eye axis Z
|
||||||
@@ -38,18 +51,18 @@ femto_mean_curr:
|
|||||||
readOnly: true
|
readOnly: true
|
||||||
softwareTrigger: false
|
softwareTrigger: false
|
||||||
|
|
||||||
es1_roty:
|
# es1_roty:
|
||||||
readoutPriority: monitored
|
# readoutPriority: monitored
|
||||||
description: 'Test rotation stage'
|
# description: 'Test rotation stage'
|
||||||
deviceClass: ophyd.EpicsMotor
|
# deviceClass: ophyd.EpicsMotor
|
||||||
deviceConfig:
|
# deviceConfig:
|
||||||
prefix: X02DA-ES1-SMP1:ROTY
|
# prefix: X02DA-ES1-SMP1:ROTY
|
||||||
deviceTags:
|
# deviceTags:
|
||||||
- es1-sam
|
# - es1-sam
|
||||||
onFailure: buffer
|
# onFailure: buffer
|
||||||
enabled: true
|
# enabled: true
|
||||||
readOnly: false
|
# readOnly: false
|
||||||
softwareTrigger: false
|
# softwareTrigger: false
|
||||||
|
|
||||||
# es1_ismc:
|
# es1_ismc:
|
||||||
# description: 'Automation1 iSMC interface'
|
# description: 'Automation1 iSMC interface'
|
||||||
@@ -64,18 +77,18 @@ es1_roty:
|
|||||||
# readoutPriority: monitored
|
# readoutPriority: monitored
|
||||||
# softwareTrigger: false
|
# softwareTrigger: false
|
||||||
|
|
||||||
es1_tasks:
|
# es1_tasks:
|
||||||
description: 'Automation1 task management interface'
|
# description: 'Automation1 task management interface'
|
||||||
deviceClass: tomcat_bec.devices.aa1Tasks
|
# deviceClass: tomcat_bec.devices.aa1Tasks
|
||||||
deviceConfig:
|
# deviceConfig:
|
||||||
prefix: 'X02DA-ES1-SMP1:TASK:'
|
# prefix: 'X02DA-ES1-SMP1:TASK:'
|
||||||
deviceTags:
|
# deviceTags:
|
||||||
- es1
|
# - es1
|
||||||
enabled: false
|
# enabled: false
|
||||||
onFailure: buffer
|
# onFailure: buffer
|
||||||
readOnly: false
|
# readOnly: false
|
||||||
readoutPriority: monitored
|
# readoutPriority: monitored
|
||||||
softwareTrigger: false
|
# softwareTrigger: false
|
||||||
|
|
||||||
|
|
||||||
# es1_psod:
|
# es1_psod:
|
||||||
@@ -92,18 +105,18 @@ es1_tasks:
|
|||||||
# softwareTrigger: true
|
# softwareTrigger: true
|
||||||
|
|
||||||
|
|
||||||
es1_ddaq:
|
# es1_ddaq:
|
||||||
description: 'Automation1 position recording interface'
|
# description: 'Automation1 position recording interface'
|
||||||
deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection
|
# deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection
|
||||||
deviceConfig:
|
# deviceConfig:
|
||||||
prefix: 'X02DA-ES1-SMP1:ROTY:DDC:'
|
# prefix: 'X02DA-ES1-SMP1:ROTY:DDC:'
|
||||||
deviceTags:
|
# deviceTags:
|
||||||
- es1
|
# - es1
|
||||||
enabled: true
|
# enabled: true
|
||||||
onFailure: buffer
|
# onFailure: buffer
|
||||||
readOnly: false
|
# readOnly: false
|
||||||
readoutPriority: monitored
|
# readoutPriority: monitored
|
||||||
softwareTrigger: false
|
# softwareTrigger: false
|
||||||
|
|
||||||
|
|
||||||
#camera:
|
#camera:
|
||||||
@@ -145,6 +158,7 @@ gfdaq:
|
|||||||
data_source_name: 'gfcam'
|
data_source_name: 'gfcam'
|
||||||
deviceTags:
|
deviceTags:
|
||||||
- std-daq
|
- std-daq
|
||||||
|
- daq
|
||||||
- gfcam
|
- gfcam
|
||||||
enabled: true
|
enabled: true
|
||||||
onFailure: buffer
|
onFailure: buffer
|
||||||
@@ -159,6 +173,7 @@ gf_stream0:
|
|||||||
url: 'tcp://129.129.95.111:20000'
|
url: 'tcp://129.129.95.111:20000'
|
||||||
deviceTags:
|
deviceTags:
|
||||||
- std-daq
|
- std-daq
|
||||||
|
- preview
|
||||||
- gfcam
|
- gfcam
|
||||||
enabled: true
|
enabled: true
|
||||||
onFailure: buffer
|
onFailure: buffer
|
||||||
@@ -187,8 +202,10 @@ pcodaq:
|
|||||||
deviceConfig:
|
deviceConfig:
|
||||||
ws_url: 'ws://129.129.95.111:8081'
|
ws_url: 'ws://129.129.95.111:8081'
|
||||||
rest_url: 'http://129.129.95.111:5010'
|
rest_url: 'http://129.129.95.111:5010'
|
||||||
|
data_source_name: 'pcocam'
|
||||||
deviceTags:
|
deviceTags:
|
||||||
- std-daq
|
- std-daq
|
||||||
|
- daq
|
||||||
- pcocam
|
- pcocam
|
||||||
enabled: true
|
enabled: true
|
||||||
onFailure: buffer
|
onFailure: buffer
|
||||||
@@ -203,6 +220,7 @@ pco_stream0:
|
|||||||
url: 'tcp://129.129.95.111:20010'
|
url: 'tcp://129.129.95.111:20010'
|
||||||
deviceTags:
|
deviceTags:
|
||||||
- std-daq
|
- std-daq
|
||||||
|
- preview
|
||||||
- pcocam
|
- pcocam
|
||||||
enabled: true
|
enabled: true
|
||||||
onFailure: buffer
|
onFailure: buffer
|
||||||
|
|||||||
@@ -41,9 +41,9 @@ class StdDaqMixin(CustomDeviceMixin):
|
|||||||
# Fish out our configuration from scaninfo (via explicit or generic addressing)
|
# Fish out our configuration from scaninfo (via explicit or generic addressing)
|
||||||
# NOTE: Scans don't have to fully configure the device
|
# NOTE: Scans don't have to fully configure the device
|
||||||
d = {}
|
d = {}
|
||||||
scan_parameters = self.parent.scaninfo.scan_msg.scan_parameters
|
# scan_parameters = self.parent.scaninfo.scan_msg.scan_parameters
|
||||||
std_daq_params = scan_parameters.get("std_daq_params")
|
# std_daq_params = scan_parameters.get("std_daq_params")
|
||||||
|
|
||||||
if "kwargs" in self.parent.scaninfo.scan_msg.info:
|
if "kwargs" in self.parent.scaninfo.scan_msg.info:
|
||||||
scanargs = self.parent.scaninfo.scan_msg.info["kwargs"]
|
scanargs = self.parent.scaninfo.scan_msg.info["kwargs"]
|
||||||
if "image_width" in scanargs and scanargs["image_width"] is not None:
|
if "image_width" in scanargs and scanargs["image_width"] is not None:
|
||||||
@@ -53,8 +53,8 @@ class StdDaqMixin(CustomDeviceMixin):
|
|||||||
if "nr_writers" in scanargs and scanargs["nr_writers"] is not None:
|
if "nr_writers" in scanargs and scanargs["nr_writers"] is not None:
|
||||||
d["nr_writers"] = scanargs["nr_writers"]
|
d["nr_writers"] = scanargs["nr_writers"]
|
||||||
if "system_config" in scanargs and scanargs["system_config"] is not None:
|
if "system_config" in scanargs and scanargs["system_config"] is not None:
|
||||||
if scanargs['system_config']['file_directory']:
|
if scanargs["system_config"]["file_directory"]:
|
||||||
file_directory = scanargs['system_config']['file_directory']
|
file_directory = scanargs["system_config"]["file_directory"]
|
||||||
### to be used in the future to substitute the procedure using file path
|
### to be used in the future to substitute the procedure using file path
|
||||||
if "file_path" in scanargs and scanargs["file_path"] is not None:
|
if "file_path" in scanargs and scanargs["file_path"] is not None:
|
||||||
self.parent.file_path.set(scanargs["file_path"].replace("data", "gpfs")).wait()
|
self.parent.file_path.set(scanargs["file_path"].replace("data", "gpfs")).wait()
|
||||||
@@ -91,7 +91,6 @@ class StdDaqMixin(CustomDeviceMixin):
|
|||||||
if points_valid:
|
if points_valid:
|
||||||
d["num_points_total"] = num_points
|
d["num_points_total"] = num_points
|
||||||
|
|
||||||
|
|
||||||
# Perform bluesky-style configuration
|
# Perform bluesky-style configuration
|
||||||
if len(d) > 0:
|
if len(d) > 0:
|
||||||
# Configure new run (will restart the stdDAQ)
|
# Configure new run (will restart the stdDAQ)
|
||||||
@@ -101,9 +100,9 @@ class StdDaqMixin(CustomDeviceMixin):
|
|||||||
sleep(0.5)
|
sleep(0.5)
|
||||||
|
|
||||||
# Try to start a new run (reconnects)
|
# Try to start a new run (reconnects)
|
||||||
if std_daq_params.get("reconnect",True):
|
# if std_daq_params.get("reconnect", True):
|
||||||
self.parent.bluestage()
|
self.parent.bluestage()
|
||||||
|
|
||||||
# And start status monitoring
|
# And start status monitoring
|
||||||
self._mon = Thread(target=self.monitor, daemon=True)
|
self._mon = Thread(target=self.monitor, daemon=True)
|
||||||
self._mon.start()
|
self._mon.start()
|
||||||
@@ -179,6 +178,7 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
file_prefix = Component(Signal, value="file", kind=Kind.config)
|
file_prefix = Component(Signal, value="file", kind=Kind.config)
|
||||||
# Configuration attributes
|
# Configuration attributes
|
||||||
rest_url = Component(Signal, kind=Kind.config, metadata={"write_access": False})
|
rest_url = Component(Signal, kind=Kind.config, metadata={"write_access": False})
|
||||||
|
datasource = Component(Signal, kind=Kind.config, metadata={"write_access": False})
|
||||||
cfg_detector_name = Component(Signal, kind=Kind.config)
|
cfg_detector_name = Component(Signal, kind=Kind.config)
|
||||||
cfg_detector_type = Component(Signal, kind=Kind.config)
|
cfg_detector_type = Component(Signal, kind=Kind.config)
|
||||||
cfg_bit_depth = Component(Signal, kind=Kind.config)
|
cfg_bit_depth = Component(Signal, kind=Kind.config)
|
||||||
@@ -213,7 +213,7 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
)
|
)
|
||||||
self.ws_url.set(ws_url, force=True).wait()
|
self.ws_url.set(ws_url, force=True).wait()
|
||||||
self.rest_url.set(rest_url, force=True).wait()
|
self.rest_url.set(rest_url, force=True).wait()
|
||||||
self.data_source_name = data_source_name
|
self.datasource.set(data_source_name, force=True).wait()
|
||||||
|
|
||||||
# Connect to the DAQ and initialize values
|
# Connect to the DAQ and initialize values
|
||||||
try:
|
try:
|
||||||
@@ -335,15 +335,6 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
sleep(1)
|
sleep(1)
|
||||||
self.get_daq_config(update=True)
|
self.get_daq_config(update=True)
|
||||||
|
|
||||||
# def configure(self, d:dict):
|
|
||||||
# if "num_points_total" in d:
|
|
||||||
# num_points = d.pop("num_points_total")
|
|
||||||
# self.num_images.set(num_points).wait()
|
|
||||||
# super().configure(d)
|
|
||||||
# self.set_daq_config()
|
|
||||||
# sleep(1)
|
|
||||||
# self.get_daq_config(update=True)
|
|
||||||
|
|
||||||
def bluestage(self):
|
def bluestage(self):
|
||||||
"""Stages the stdDAQ
|
"""Stages the stdDAQ
|
||||||
|
|
||||||
@@ -355,27 +346,12 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
if self.state() != "idle":
|
if self.state() != "idle":
|
||||||
raise RuntimeError(f"[{self.name}] stdDAQ can't stage from state: {self.state()}")
|
raise RuntimeError(f"[{self.name}] stdDAQ can't stage from state: {self.state()}")
|
||||||
|
|
||||||
# Must make sure that image size matches the data source
|
# Ensure expected shape
|
||||||
if self.data_source_name is not None:
|
self.validate()
|
||||||
cam_img_w = self.device_manager.devices[self.data_source_name].cfgRoiX.get()
|
|
||||||
cam_img_h = self.device_manager.devices[self.data_source_name].cfgRoiY.get()
|
|
||||||
daq_img_w = self.cfg_pixel_width.get()
|
|
||||||
daq_img_h = self.cfg_pixel_height.get()
|
|
||||||
|
|
||||||
if not (daq_img_w == cam_img_w and daq_img_h == cam_img_h):
|
|
||||||
raise RuntimeError(
|
|
||||||
f"[{self.name}] stdDAQ image resolution ({daq_img_w} , {daq_img_h}) does not match camera with ({cam_img_w} , {cam_img_h})"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.warning(
|
|
||||||
f"[{self.name}] stdDAQ image resolution ({daq_img_w} , {daq_img_h}) matches camera with ({cam_img_w} , {cam_img_h})"
|
|
||||||
)
|
|
||||||
|
|
||||||
file_path = self.file_path.get()
|
file_path = self.file_path.get()
|
||||||
num_images = self.num_images.get()
|
|
||||||
file_prefix = self.name
|
|
||||||
file_prefix = self.file_prefix.get()
|
file_prefix = self.file_prefix.get()
|
||||||
print(file_prefix)
|
num_images = self.num_images.get()
|
||||||
|
|
||||||
# New connection
|
# New connection
|
||||||
self._wsclient = self.connect()
|
self._wsclient = self.connect()
|
||||||
@@ -407,9 +383,7 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
print(f"[{self.name}] Started stdDAQ in: {reply['status']}")
|
print(f"[{self.name}] Started stdDAQ in: {reply['status']}")
|
||||||
return
|
return
|
||||||
|
|
||||||
raise RuntimeError(
|
raise RuntimeError(f"[{self.name}] Failed to start the stdDAQ, reason: {reply['reason']}")
|
||||||
f"[{self.name}] Failed to start the stdDAQ in 1 tries, reason: {reply['reason']}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def blueunstage(self):
|
def blueunstage(self):
|
||||||
"""Unstages the stdDAQ
|
"""Unstages the stdDAQ
|
||||||
@@ -464,6 +438,25 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
status = SubscriptionStatus(self.runstatus, is_running, settle_time=0.5)
|
status = SubscriptionStatus(self.runstatus, is_running, settle_time=0.5)
|
||||||
return status
|
return status
|
||||||
|
|
||||||
|
def validate(self):
|
||||||
|
"""Validate camera state
|
||||||
|
|
||||||
|
Ensure that data source shape matches with the shape expected by the stdDAQ.
|
||||||
|
"""
|
||||||
|
# Must make sure that image size matches the data source
|
||||||
|
source = self.datasource.get()
|
||||||
|
if source is not None and len(source) > 0:
|
||||||
|
if source == "gfcam":
|
||||||
|
cam_img_w = self.device_manager.devices[source].cfgRoiX.get()
|
||||||
|
cam_img_h = self.device_manager.devices[source].cfgRoiY.get()
|
||||||
|
daq_img_w = self.cfg_pixel_width.get()
|
||||||
|
daq_img_h = self.cfg_pixel_height.get()
|
||||||
|
|
||||||
|
if not (daq_img_w == cam_img_w and daq_img_h == cam_img_h):
|
||||||
|
raise RuntimeError(
|
||||||
|
f"[{self.name}] stdDAQ image resolution ({daq_img_w} , {daq_img_h}) does not match camera with ({cam_img_w} , {cam_img_h})"
|
||||||
|
)
|
||||||
|
|
||||||
def get_daq_config(self, update=False) -> dict:
|
def get_daq_config(self, update=False) -> dict:
|
||||||
"""Read the current configuration from the DAQ"""
|
"""Read the current configuration from the DAQ"""
|
||||||
r = requests.get(self.rest_url.get() + "/api/config/get", params={"user": "ioc"}, timeout=2)
|
r = requests.get(self.rest_url.get() + "/api/config/get", params={"user": "ioc"}, timeout=2)
|
||||||
|
|||||||
@@ -23,107 +23,23 @@ logger = bec_logger.logger
|
|||||||
ZMQ_TOPIC_FILTER = b''
|
ZMQ_TOPIC_FILTER = b''
|
||||||
|
|
||||||
|
|
||||||
class StdDaqPreviewState(enum.IntEnum):
|
|
||||||
"""Standard DAQ ophyd device states"""
|
|
||||||
UNKNOWN = 0
|
|
||||||
DETACHED = 1
|
|
||||||
MONITORING = 2
|
|
||||||
|
|
||||||
|
|
||||||
class StdDaqPreviewMixin(CustomDetectorMixin):
|
class StdDaqPreviewMixin(CustomDetectorMixin):
|
||||||
"""Setup class for the standard DAQ preview stream
|
"""Setup class for the standard DAQ preview stream
|
||||||
|
|
||||||
Parent class: CustomDetectorMixin
|
Parent class: CustomDetectorMixin
|
||||||
"""
|
"""
|
||||||
_mon = None
|
|
||||||
|
|
||||||
def on_stage(self):
|
def on_stage(self):
|
||||||
"""Start listening for preview data stream"""
|
"""Start listening for preview data stream"""
|
||||||
if self._mon is not None:
|
self.parent.arm()
|
||||||
self.parent.unstage()
|
|
||||||
sleep(0.5)
|
|
||||||
|
|
||||||
self.parent.connect()
|
|
||||||
self._stop_polling = False
|
|
||||||
self._mon = Thread(target=self.poll, daemon=True)
|
|
||||||
self._mon.start()
|
|
||||||
|
|
||||||
def on_unstage(self):
|
def on_unstage(self):
|
||||||
"""Stop a running preview"""
|
"""Stop a running preview"""
|
||||||
if self._mon is not None:
|
self.parent.disarm()
|
||||||
self._stop_polling = True
|
|
||||||
# Might hang on recv_multipart
|
|
||||||
self._mon.join(timeout=1)
|
|
||||||
# So also disconnect the socket
|
|
||||||
self.parent._socket.disconnect(self.parent.url.get())
|
|
||||||
|
|
||||||
def on_stop(self):
|
def on_stop(self):
|
||||||
"""Stop a running preview"""
|
"""Stop a running preview"""
|
||||||
self.on_unstage()
|
self.on_unstage()
|
||||||
|
|
||||||
def poll(self):
|
|
||||||
"""Collect streamed updates"""
|
|
||||||
self.parent.status.set(StdDaqPreviewState.MONITORING, force=True)
|
|
||||||
try:
|
|
||||||
t_last = time()
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
# Exit loop and finish monitoring
|
|
||||||
if self._stop_polling:
|
|
||||||
logger.info(f"[{self.parent.name}]\tDetaching monitor")
|
|
||||||
break
|
|
||||||
|
|
||||||
# pylint: disable=no-member
|
|
||||||
r = self.parent._socket.recv_multipart(flags=zmq.NOBLOCK)
|
|
||||||
|
|
||||||
# Length and throtling checks
|
|
||||||
if len(r) != 2:
|
|
||||||
logger.warning(
|
|
||||||
f"[{self.parent.name}] Received malformed array of length {len(r)}")
|
|
||||||
t_curr = time()
|
|
||||||
t_elapsed = t_curr - t_last
|
|
||||||
if t_elapsed < self.parent.throttle.get():
|
|
||||||
sleep(0.1)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Unpack the Array V1 reply to metadata and array data
|
|
||||||
meta, data = r
|
|
||||||
|
|
||||||
# Update image and update subscribers
|
|
||||||
header = json.loads(meta)
|
|
||||||
if header["type"] == "uint16":
|
|
||||||
image = np.frombuffer(data, dtype=np.uint16)
|
|
||||||
if image.size != np.prod(header['shape']):
|
|
||||||
err = f"Unexpected array size of {image.size} for header: {header}"
|
|
||||||
raise ValueError(err)
|
|
||||||
image = image.reshape(header['shape'])
|
|
||||||
|
|
||||||
# Update image and update subscribers
|
|
||||||
self.parent.frame.put(header['frame'], force=True)
|
|
||||||
self.parent.image_shape.put(header['shape'], force=True)
|
|
||||||
self.parent.image.put(image, force=True)
|
|
||||||
self.parent._last_image = image
|
|
||||||
self.parent._run_subs(sub_type=self.parent.SUB_MONITOR, value=image)
|
|
||||||
t_last = t_curr
|
|
||||||
logger.info(
|
|
||||||
f"[{self.parent.name}] Updated frame {header['frame']}\t"
|
|
||||||
f"Shape: {header['shape']}\tMean: {np.mean(image):.3f}"
|
|
||||||
)
|
|
||||||
except ValueError:
|
|
||||||
# Happens when ZMQ partially delivers the multipart message
|
|
||||||
pass
|
|
||||||
except zmq.error.Again:
|
|
||||||
# Happens when receive queue is empty
|
|
||||||
sleep(0.1)
|
|
||||||
except Exception as ex:
|
|
||||||
logger.info(f"[{self.parent.name}]\t{str(ex)}")
|
|
||||||
raise
|
|
||||||
finally:
|
|
||||||
self._mon = None
|
|
||||||
self.parent.status.set(StdDaqPreviewState.DETACHED, force=True)
|
|
||||||
logger.info(f"[{self.parent.name}]\tDetaching monitor")
|
|
||||||
|
|
||||||
|
|
||||||
class StdDaqPreviewDetector(PSIDetectorBase):
|
class StdDaqPreviewDetector(PSIDetectorBase):
|
||||||
"""Detector wrapper class around the StdDaq preview image stream.
|
"""Detector wrapper class around the StdDaq preview image stream.
|
||||||
|
|
||||||
@@ -135,7 +51,7 @@ class StdDaqPreviewDetector(PSIDetectorBase):
|
|||||||
cam_widget = gui.add_dock('cam_dock1').add_widget('BECFigure').image('daq_stream1')
|
cam_widget = gui.add_dock('cam_dock1').add_widget('BECFigure').image('daq_stream1')
|
||||||
"""
|
"""
|
||||||
# Subscriptions for plotting image
|
# Subscriptions for plotting image
|
||||||
USER_ACCESS = ["kickoff", "get_last_image"]
|
USER_ACCESS = ["arm", "disarm", "get_last_image"]
|
||||||
SUB_MONITOR = "device_monitor_2d"
|
SUB_MONITOR = "device_monitor_2d"
|
||||||
_default_sub = SUB_MONITOR
|
_default_sub = SUB_MONITOR
|
||||||
|
|
||||||
@@ -144,19 +60,19 @@ class StdDaqPreviewDetector(PSIDetectorBase):
|
|||||||
# Status attributes
|
# Status attributes
|
||||||
url = Component(Signal, kind=Kind.config)
|
url = Component(Signal, kind=Kind.config)
|
||||||
throttle = Component(Signal, value=0.25, kind=Kind.config)
|
throttle = Component(Signal, value=0.25, kind=Kind.config)
|
||||||
status = Component(Signal, value=StdDaqPreviewState.UNKNOWN, kind=Kind.omitted)
|
|
||||||
frame = Component(Signal, kind=Kind.hinted)
|
frame = Component(Signal, kind=Kind.hinted)
|
||||||
image_shape = Component(Signal, kind=Kind.normal)
|
image_shape = Component(Signal, kind=Kind.normal)
|
||||||
# FIXME: The BEC client caches the read()s from the last 50 scans
|
# FIXME: The BEC client caches the read()s from the last 50 scans
|
||||||
image = Component(Signal, kind=Kind.omitted)
|
image = Component(Signal, kind=Kind.omitted)
|
||||||
_last_image = None
|
_last_image = None
|
||||||
|
_stop_polling = True
|
||||||
|
_mon = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, *args, url: str = "tcp://129.129.95.38:20000", parent: Device = None, **kwargs
|
self, *args, url: str = "tcp://129.129.95.38:20000", parent: Device = None, **kwargs
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(*args, parent=parent, **kwargs)
|
super().__init__(*args, parent=parent, **kwargs)
|
||||||
self.url._metadata["write_access"] = False
|
self.url._metadata["write_access"] = False
|
||||||
self.status._metadata["write_access"] = False
|
|
||||||
self.image._metadata["write_access"] = False
|
self.image._metadata["write_access"] = False
|
||||||
self.frame._metadata["write_access"] = False
|
self.frame._metadata["write_access"] = False
|
||||||
self.image_shape._metadata["write_access"] = False
|
self.image_shape._metadata["write_access"] = False
|
||||||
@@ -185,9 +101,92 @@ class StdDaqPreviewDetector(PSIDetectorBase):
|
|||||||
def get_image(self):
|
def get_image(self):
|
||||||
return self._last_image
|
return self._last_image
|
||||||
|
|
||||||
def kickoff(self) -> DeviceStatus:
|
def arm(self):
|
||||||
""" The DAQ was not meant to be toggled"""
|
"""Start listening for preview data stream"""
|
||||||
return DeviceStatus(self, done=True, success=True, settle_time=0.1)
|
if self._mon is not None:
|
||||||
|
self.unstage()
|
||||||
|
sleep(0.5)
|
||||||
|
|
||||||
|
self.connect()
|
||||||
|
self._stop_polling = False
|
||||||
|
self._mon = Thread(target=self.poll, daemon=True)
|
||||||
|
self._mon.start()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def disarm(self):
|
||||||
|
"""Stop a running preview"""
|
||||||
|
if self._mon is not None:
|
||||||
|
self._stop_polling = True
|
||||||
|
# Might hang on recv_multipart
|
||||||
|
self._mon.join(timeout=1)
|
||||||
|
# So also disconnect the socket (if not already disconnected)
|
||||||
|
try:
|
||||||
|
self._socket.disconnect(self.url.get())
|
||||||
|
except zmq.error.ZMQError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def poll(self):
|
||||||
|
"""Collect streamed updates"""
|
||||||
|
try:
|
||||||
|
t_last = time()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
# Exit loop and finish monitoring
|
||||||
|
if self._stop_polling:
|
||||||
|
break
|
||||||
|
|
||||||
|
# pylint: disable=no-member
|
||||||
|
r = self._socket.recv_multipart(flags=zmq.NOBLOCK)
|
||||||
|
|
||||||
|
# Length and throtling checks
|
||||||
|
if len(r) != 2:
|
||||||
|
logger.warning(
|
||||||
|
f"[{self.name}] Received malformed array of length {len(r)}")
|
||||||
|
t_curr = time()
|
||||||
|
t_elapsed = t_curr - t_last
|
||||||
|
if t_elapsed < self.throttle.get():
|
||||||
|
sleep(0.1)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Unpack the Array V1 reply to metadata and array data
|
||||||
|
meta, data = r
|
||||||
|
|
||||||
|
# Update image and update subscribers
|
||||||
|
header = json.loads(meta)
|
||||||
|
image = None
|
||||||
|
if header["type"] == "uint16":
|
||||||
|
image = np.frombuffer(data, dtype=np.uint16)
|
||||||
|
|
||||||
|
if image.size != np.prod(header['shape']):
|
||||||
|
err = f"Unexpected array size of {image.size} for header: {header}"
|
||||||
|
raise ValueError(err)
|
||||||
|
image = image.reshape(header['shape'])
|
||||||
|
|
||||||
|
# Update image and update subscribers
|
||||||
|
self.frame.put(header['frame'], force=True)
|
||||||
|
self.image_shape.put(header['shape'], force=True)
|
||||||
|
self.image.put(image, force=True)
|
||||||
|
self._last_image = image
|
||||||
|
self._run_subs(sub_type=self.SUB_MONITOR, value=image)
|
||||||
|
t_last = t_curr
|
||||||
|
logger.info(
|
||||||
|
f"[{self.name}] Updated frame {header['frame']}\t"
|
||||||
|
f"Shape: {header['shape']}\tMean: {np.mean(image):.3f}"
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
# Happens when ZMQ partially delivers the multipart message
|
||||||
|
pass
|
||||||
|
except zmq.error.Again:
|
||||||
|
# Happens when receive queue is empty
|
||||||
|
sleep(0.1)
|
||||||
|
except Exception as ex:
|
||||||
|
logger.info(f"[{self.name}]\t{str(ex)}")
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
self._mon = None
|
||||||
|
logger.info(f"[{self.name}]\tDetaching monitor")
|
||||||
|
|
||||||
|
|
||||||
# Automatically connect to MicroSAXS testbench if directly invoked
|
# Automatically connect to MicroSAXS testbench if directly invoked
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
from .tutorial_fly_scan import AcquireDark, AcquireWhite, AcquireRefs, AcquireProjections, TutorialFlyScanContLine
|
from .tutorial_fly_scan import AcquireDark, AcquireWhite, AcquireRefs, AcquireProjections, TutorialFlyScanContLine
|
||||||
from .tomcat_scans import TomcatSnapNStep, TomcatSimpleSequence
|
from .tomcat_scans import TomcatSnapNStep, TomcatSimpleSequence
|
||||||
|
from .advanced_scans import AcquireRefsV2, AcquireDarkV2, AcquireWhiteV2
|
||||||
|
|||||||
@@ -0,0 +1,383 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from bec_lib.device import DeviceBase
|
||||||
|
from bec_server.scan_server.scans import Acquire, AsyncFlyScanBase
|
||||||
|
|
||||||
|
from bec_lib import bec_logger
|
||||||
|
|
||||||
|
logger = bec_logger.logger
|
||||||
|
|
||||||
|
|
||||||
|
class Shutter:
|
||||||
|
""" Shutter status """
|
||||||
|
CLOSED = 0
|
||||||
|
OPEN = 1
|
||||||
|
|
||||||
|
|
||||||
|
class AcquireDarkV2(Acquire):
|
||||||
|
scan_name = "acquire_dark_v2"
|
||||||
|
required_kwargs = ["exp_burst"]
|
||||||
|
gui_config = {"Acquisition parameters": ["exp_burst"]}
|
||||||
|
|
||||||
|
def __init__(self, exp_burst: int, file_prefix="", **kwargs):
|
||||||
|
"""
|
||||||
|
Acquire dark images. This scan is used to acquire dark images. Dark images are images taken with the shutter
|
||||||
|
closed and no beam on the sample. Dark images are used to correct the data images for dark current.
|
||||||
|
|
||||||
|
NOTE: this scan has a special operation mode that does not call
|
||||||
|
|
||||||
|
Args:
|
||||||
|
exp_burst : int
|
||||||
|
Number of dark images to acquire (no default)
|
||||||
|
file_prefix : str
|
||||||
|
File prefix
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> scans.acquire_dark(5)
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(exp_burst=exp_burst, file_prefix="", **kwargs)
|
||||||
|
self.burst_at_each_point = 1 # At each point, how many times I want to individually trigger
|
||||||
|
self.exp_burst = exp_burst
|
||||||
|
self.file_prefix = file_prefix
|
||||||
|
|
||||||
|
def pre_scan(self):
|
||||||
|
""" Close the shutter before scan"""
|
||||||
|
yield from self.stubs.set(device=["eyex"], value=[Shutter.CLOSED])
|
||||||
|
return super().pre_scan()
|
||||||
|
|
||||||
|
def direct(self):
|
||||||
|
""" Direct scan procedure call"""
|
||||||
|
# Collect relevant devices
|
||||||
|
self.cams = [cam.name for cam in self.device_manager.devices.get_devices_with_tags("camera") if cam.enabled]
|
||||||
|
self.prev = [pre.name for pre in self.device_manager.devices.get_devices_with_tags("preview") if pre.enabled]
|
||||||
|
self.daqs = [daq.name for daq in self.device_manager.devices.get_devices_with_tags("daq") if daq.enabled]
|
||||||
|
|
||||||
|
# Do not call stage, as there's no ScanInfo emitted for direct call
|
||||||
|
for daq in self.daqs:
|
||||||
|
cam = yield from self.stubs.send_rpc_and_wait(daq, "datasource.get")
|
||||||
|
prefix = f"{self.file_prefix}_{cam}_dark"
|
||||||
|
yield from self.stubs.send_rpc_and_wait(daq, "file_prefix.set", prefix)
|
||||||
|
yield from self.stubs.send_rpc_and_wait(daq, "num_images.set", self.exp_burst)
|
||||||
|
yield from self.stubs.send_rpc_and_wait(daq, "bluestage")
|
||||||
|
for prev in self.prev:
|
||||||
|
yield from self.stubs.send_rpc_and_wait(prev, "arm")
|
||||||
|
for cam in self.cams:
|
||||||
|
yield from self.stubs.send_rpc_and_wait(cam, "configure", {'exposure_num_burst': self.exp_burst})
|
||||||
|
yield from self.stubs.send_rpc_and_wait(cam, "bluestage")
|
||||||
|
|
||||||
|
yield from self.pre_scan()
|
||||||
|
yield from self.scan_core()
|
||||||
|
yield from self.finalize()
|
||||||
|
yield from self.unstage()
|
||||||
|
yield from self.cleanup()
|
||||||
|
|
||||||
|
|
||||||
|
class AcquireWhiteV2(Acquire):
|
||||||
|
scan_name = "acquire_white_v2"
|
||||||
|
gui_config = {"Acquisition parameters": ["exp_burst"]}
|
||||||
|
|
||||||
|
def __init__(self, motor: DeviceBase, exp_burst: int, sample_position_out: float, sample_angle_out: float, file_prefix: str="", **kwargs):
|
||||||
|
"""
|
||||||
|
Acquire flat field images. This scan is used to acquire flat field images. The flat field image is an image taken
|
||||||
|
with the shutter open but the sample out of the beam. Flat field images are used to correct the data images for
|
||||||
|
non-uniformity in the detector.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
motor : DeviceBase
|
||||||
|
Motor to be moved to move the sample out of beam
|
||||||
|
exp_burst : int
|
||||||
|
Number of flat field images to acquire (no default)
|
||||||
|
sample_position_out : float
|
||||||
|
Position to move the sample stage to position the sample out of beam and take flat field images
|
||||||
|
sample_angle_out : float
|
||||||
|
Angular position where to take the flat field images
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> scans.acquire_white(dev.samx, 5, 20)
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(exp_burst=exp_burst, **kwargs)
|
||||||
|
self.exp_burst = exp_burst
|
||||||
|
self.file_prefix = file_prefix
|
||||||
|
self.burst_at_each_point = 1
|
||||||
|
|
||||||
|
self.scan_motors = [motor, "eyez"]
|
||||||
|
# self.scan_motors = [motor, "es1_roty"]
|
||||||
|
self.out_position = [sample_position_out, sample_angle_out]
|
||||||
|
|
||||||
|
def pre_scan(self):
|
||||||
|
""" Open the shutter before scan"""
|
||||||
|
# Move sample out
|
||||||
|
yield from self._move_scan_motors_and_wait(self.out_position)
|
||||||
|
# Open the main shutter (TODO change to the correct shutter device)
|
||||||
|
yield from self.stubs.set(device=["eyex"], value=[Shutter.OPEN])
|
||||||
|
|
||||||
|
return super().pre_scan()
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
""" Close the shutter after scan"""
|
||||||
|
# Close fast shutter
|
||||||
|
yield from self.stubs.set(device=["eyex"], value=[Shutter.CLOSED])
|
||||||
|
return super().cleanup()
|
||||||
|
|
||||||
|
def direct(self):
|
||||||
|
""" Direct scan procedure call"""
|
||||||
|
# Collect relevant devices
|
||||||
|
self.cams = [cam.name for cam in self.device_manager.devices.get_devices_with_tags("camera") if cam.enabled]
|
||||||
|
self.prev = [pre.name for pre in self.device_manager.devices.get_devices_with_tags("preview") if pre.enabled]
|
||||||
|
self.daqs = [daq.name for daq in self.device_manager.devices.get_devices_with_tags("daq") if daq.enabled]
|
||||||
|
|
||||||
|
# Do not call stage, as there's no ScanInfo emitted for direct call
|
||||||
|
for daq in self.daqs:
|
||||||
|
cam = yield from self.stubs.send_rpc_and_wait(daq, "datasource.get")
|
||||||
|
prefix = f"{self.file_prefix}_{cam}_white"
|
||||||
|
yield from self.stubs.send_rpc_and_wait(daq, "file_prefix.set", prefix)
|
||||||
|
yield from self.stubs.send_rpc_and_wait(daq, "num_images.set", self.exp_burst)
|
||||||
|
yield from self.stubs.send_rpc_and_wait(daq, "bluestage")
|
||||||
|
for prev in self.prev:
|
||||||
|
yield from self.stubs.send_rpc_and_wait(prev, "arm")
|
||||||
|
for cam in self.cams:
|
||||||
|
yield from self.stubs.send_rpc_and_wait(cam, "configure", {'exposure_num_burst': self.exp_burst})
|
||||||
|
yield from self.stubs.send_rpc_and_wait(cam, "bluestage")
|
||||||
|
|
||||||
|
yield from self.pre_scan()
|
||||||
|
yield from self.scan_core()
|
||||||
|
yield from self.finalize()
|
||||||
|
yield from self.unstage()
|
||||||
|
yield from self.cleanup()
|
||||||
|
|
||||||
|
|
||||||
|
# class AcquireProjections(AsyncFlyScanBase):
|
||||||
|
# scan_name = "acquire_projections"
|
||||||
|
# gui_config = {
|
||||||
|
# "Motor": ["motor"],
|
||||||
|
# "Acquisition parameters": ["sample_position_in", "start_angle", "angular_range" ],
|
||||||
|
# "Camera": ["exp_time", "exp_burst"]
|
||||||
|
# }
|
||||||
|
|
||||||
|
# def __init__(self,
|
||||||
|
# motor: DeviceBase,
|
||||||
|
# exp_burst: int,
|
||||||
|
# sample_position_in: float,
|
||||||
|
# start_angle: float,
|
||||||
|
# angular_range: float,
|
||||||
|
# exp_time:float,
|
||||||
|
# **kwargs):
|
||||||
|
# """
|
||||||
|
# Acquire projection images.
|
||||||
|
|
||||||
|
# Args:
|
||||||
|
# motor : DeviceBase
|
||||||
|
# Motor to move continuously from start to stop position
|
||||||
|
# exp_burst : int
|
||||||
|
# Number of flat field images to acquire (no default)
|
||||||
|
# sample_position_in : float
|
||||||
|
# Position to move the sample stage to position the sample in the beam
|
||||||
|
# start_angle : float
|
||||||
|
# Angular start position for the scan
|
||||||
|
# angular_range : float
|
||||||
|
# Angular range
|
||||||
|
# exp_time : float, optional
|
||||||
|
# Exposure time [ms]. If not specified, the currently configured value on the camera will be used
|
||||||
|
# exp_period : float, optional
|
||||||
|
# Exposure period [ms]. If not specified, the currently configured value on the camera will be used
|
||||||
|
# image_width : int, optional
|
||||||
|
# ROI size in the x-direction [pixels]. If not specified, the currently configured value on the camera will be used
|
||||||
|
# image_height : int, optional
|
||||||
|
# ROI size in the y-direction [pixels]. If not specified, the currently configured value on the camera will be used
|
||||||
|
# acq_mode : str, optional
|
||||||
|
# Predefined acquisition mode (default= 'default')
|
||||||
|
# file_path : str, optional
|
||||||
|
# File path for standard daq
|
||||||
|
# ddc_trigger : int, optional
|
||||||
|
# Drive Data Capture Trigger
|
||||||
|
# ddc_source0 : int, optional
|
||||||
|
# Drive Data capture Input0
|
||||||
|
|
||||||
|
# Returns:
|
||||||
|
# ScanReport
|
||||||
|
|
||||||
|
# Examples:
|
||||||
|
# >>> scans.acquire_projections()
|
||||||
|
|
||||||
|
# """
|
||||||
|
# self.motor = motor
|
||||||
|
# super().__init__(exp_time=exp_time,**kwargs)
|
||||||
|
|
||||||
|
# self.burst_at_each_point = 1
|
||||||
|
# self.sample_position_in = sample_position_in
|
||||||
|
# self.start_angle = start_angle
|
||||||
|
# self.angular_range = angular_range
|
||||||
|
|
||||||
|
# self.dark_shutter_pos_out = 1 ### change with a variable
|
||||||
|
# self.dark_shutter_pos_in = 0 ### change with a variable
|
||||||
|
|
||||||
|
# def update_scan_motors(self):
|
||||||
|
# return [self.motor]
|
||||||
|
|
||||||
|
# def prepare_positions(self):
|
||||||
|
# self.positions = np.array([[self.start_angle], [self.start_angle+self.angular_range]])
|
||||||
|
# self.num_pos = None
|
||||||
|
# yield from self._set_position_offset()
|
||||||
|
|
||||||
|
# def scan_core(self):
|
||||||
|
|
||||||
|
# # move to in position and go to start angular position
|
||||||
|
# yield from self.stubs.set(device=["eyez", self.motor], value=[self.sample_position_in, self.positions[0][0]])
|
||||||
|
|
||||||
|
# # open the shutter
|
||||||
|
# yield from self.stubs.set(device="eyex", value=self.dark_shutter_pos_out)
|
||||||
|
# # TODO add opening of fast shutter
|
||||||
|
|
||||||
|
# # start the flyer
|
||||||
|
# flyer_request = yield from self.stubs.set(
|
||||||
|
# device=self.motor, value=self.positions[1][0], wait=False
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.connector.send_client_info(
|
||||||
|
# "Starting the scan", show_asap=True, rid=self.metadata.get("RID")
|
||||||
|
# )
|
||||||
|
|
||||||
|
# yield from self.stubs.trigger()
|
||||||
|
|
||||||
|
# while not flyer_request.done:
|
||||||
|
|
||||||
|
# yield from self.stubs.read(
|
||||||
|
# group="monitored", point_id=self.point_id
|
||||||
|
# )
|
||||||
|
# time.sleep(1)
|
||||||
|
|
||||||
|
# # increase the point id
|
||||||
|
# self.point_id += 1
|
||||||
|
|
||||||
|
# self.num_pos = self.point_id
|
||||||
|
|
||||||
|
|
||||||
|
class AcquireRefsV2(Acquire):
|
||||||
|
scan_name = "acquire_refs_v2"
|
||||||
|
gui_config = {}
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
motor: DeviceBase,
|
||||||
|
num_darks: int = 0,
|
||||||
|
num_flats: int = 0,
|
||||||
|
sample_angle_out: float = 0,
|
||||||
|
sample_position_in: float = 0,
|
||||||
|
sample_position_out: float = 1,
|
||||||
|
file_prefix_dark: str = 'tmp_dark',
|
||||||
|
file_prefix_white: str = 'tmp_white',
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Acquire reference images (darks + whites) and return to beam position.
|
||||||
|
|
||||||
|
Reference images are acquired automatically in an optimized sequence and
|
||||||
|
the sample is returned to the sample_in_position afterwards.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
motor : DeviceBase
|
||||||
|
Motor to be moved to move the sample out of beam
|
||||||
|
num_darks : int , optional
|
||||||
|
Number of dark field images to acquire
|
||||||
|
num_flats : int , optional
|
||||||
|
Number of white field images to acquire
|
||||||
|
sample_angle_out : float , optional
|
||||||
|
Angular position where to take the flat field images
|
||||||
|
sample_position_in : float , optional
|
||||||
|
Sample stage X position for sample in beam [um]
|
||||||
|
sample_position_out : float ,optional
|
||||||
|
Sample stage X position for sample out of the beam [um]
|
||||||
|
exp_time : float, optional
|
||||||
|
Exposure time [ms]. If not specified, the currently configured value
|
||||||
|
on the camera will be used
|
||||||
|
exp_period : float, optional
|
||||||
|
Exposure period [ms]. If not specified, the currently configured value
|
||||||
|
on the camera will be used
|
||||||
|
image_width : int, optional
|
||||||
|
ROI size in the x-direction [pixels]. If not specified, the currently
|
||||||
|
configured value on the camera will be used
|
||||||
|
image_height : int, optional
|
||||||
|
ROI size in the y-direction [pixels]. If not specified, the currently
|
||||||
|
configured value on the camera will be used
|
||||||
|
acq_mode : str, optional
|
||||||
|
Predefined acquisition mode (default= 'default')
|
||||||
|
file_path : str, optional
|
||||||
|
File path for standard daq
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
ScanReport
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> scans.acquire_refs(sample_angle_out=90, sample_position_in=10, num_darks=5, num_flats=5, exp_time=0.1)
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.motor = motor
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.sample_position_in = sample_position_in
|
||||||
|
self.sample_position_out = sample_position_out
|
||||||
|
self.sample_angle_out = sample_angle_out
|
||||||
|
self.num_darks = num_darks
|
||||||
|
self.num_flats = num_flats
|
||||||
|
self.file_prefix_dark = file_prefix_dark
|
||||||
|
self.file_prefix_white = file_prefix_white
|
||||||
|
self.scan_parameters["std_daq_params"] = {"reconnect": False}
|
||||||
|
|
||||||
|
def stage(self):
|
||||||
|
"""Wrapped scan doesn't need staging"""
|
||||||
|
yield None
|
||||||
|
|
||||||
|
def scan_core(self):
|
||||||
|
|
||||||
|
if self.num_darks:
|
||||||
|
msg = f"Acquiring {self.num_darks} dark images"
|
||||||
|
logger.warning(msg)
|
||||||
|
self.connector.send_client_info(
|
||||||
|
msg,
|
||||||
|
show_asap=True,
|
||||||
|
rid=self.metadata.get("RID"),
|
||||||
|
)
|
||||||
|
|
||||||
|
darks = AcquireDarkV2(
|
||||||
|
exp_burst=self.num_darks,
|
||||||
|
# file_prefix=self.file_prefix_dark,
|
||||||
|
device_manager=self.device_manager,
|
||||||
|
metadata=self.metadata,
|
||||||
|
instruction_handler=self.stubs._instruction_handler,
|
||||||
|
**self.caller_kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
yield from darks.direct()
|
||||||
|
self.point_id = darks.point_id
|
||||||
|
|
||||||
|
|
||||||
|
if self.num_flats:
|
||||||
|
msg = f"Acquiring {self.num_flats} flat field images"
|
||||||
|
logger.warning(msg)
|
||||||
|
self.connector.send_client_info(
|
||||||
|
msg,
|
||||||
|
show_asap=True,
|
||||||
|
rid=self.metadata.get("RID"),
|
||||||
|
)
|
||||||
|
logger.warning("Calling AcquireWhite")
|
||||||
|
|
||||||
|
flats = AcquireWhiteV2(
|
||||||
|
motor=self.motor,
|
||||||
|
exp_burst=self.num_flats,
|
||||||
|
sample_position_out=self.sample_position_out,
|
||||||
|
# sample_angle_out=self.sample_angle_out,
|
||||||
|
device_manager=self.device_manager,
|
||||||
|
metadata=self.metadata,
|
||||||
|
instruction_handler=self.stubs._instruction_handler,
|
||||||
|
**self.caller_kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
flats.point_id = self.point_id
|
||||||
|
yield from flats.direct()
|
||||||
|
self.point_id = flats.point_id
|
||||||
|
## TODO move sample in beam and do not wait
|
||||||
|
## TODO move rotation to angle and do not wait
|
||||||
|
logger.warning("[AcquireRefsV2] Done with scan_core")
|
||||||
|
|
||||||
Reference in New Issue
Block a user