Linting to 9.22
This commit is contained in:
@@ -26,9 +26,11 @@ except ModuleNotFoundError:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from bec_lib import bec_logger
|
from bec_lib import bec_logger
|
||||||
|
|
||||||
logger = bec_logger.logger
|
logger = bec_logger.logger
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
logger = logging.getLogger("GfCam")
|
logger = logging.getLogger("GfCam")
|
||||||
|
|
||||||
|
|
||||||
@@ -38,8 +40,9 @@ class GigaFrostCameraMixin(CustomDetectorMixin):
|
|||||||
This class will be called by the custom_prepare_cls attribute of the
|
This class will be called by the custom_prepare_cls attribute of the
|
||||||
detector class.
|
detector class.
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=protected-access
|
||||||
def _define_backend_ip(self):
|
def _define_backend_ip(self):
|
||||||
""" Select backend IP address for UDP stream"""
|
"""Select backend IP address for UDP stream"""
|
||||||
if self.parent.backendUrl.get() == const.BE3_DAFL_CLIENT: # xbl-daq-33
|
if self.parent.backendUrl.get() == const.BE3_DAFL_CLIENT: # xbl-daq-33
|
||||||
return const.BE3_NORTH_IP, const.BE3_SOUTH_IP
|
return const.BE3_NORTH_IP, const.BE3_SOUTH_IP
|
||||||
if self.parent.backendUrl.get() == const.BE999_DAFL_CLIENT:
|
if self.parent.backendUrl.get() == const.BE999_DAFL_CLIENT:
|
||||||
@@ -48,7 +51,7 @@ class GigaFrostCameraMixin(CustomDetectorMixin):
|
|||||||
raise RuntimeError(f"Backend {self.parent.backendUrl.get()} not recognized.")
|
raise RuntimeError(f"Backend {self.parent.backendUrl.get()} not recognized.")
|
||||||
|
|
||||||
def _define_backend_mac(self):
|
def _define_backend_mac(self):
|
||||||
""" Select backend MAC address for UDP stream"""
|
"""Select backend MAC address for UDP stream"""
|
||||||
if self.parent.backendUrl.get() == const.BE3_DAFL_CLIENT: # xbl-daq-33
|
if self.parent.backendUrl.get() == const.BE3_DAFL_CLIENT: # xbl-daq-33
|
||||||
return const.BE3_NORTH_MAC, const.BE3_SOUTH_MAC
|
return const.BE3_NORTH_MAC, const.BE3_SOUTH_MAC
|
||||||
if self.parent.backendUrl.get() == const.BE999_DAFL_CLIENT:
|
if self.parent.backendUrl.get() == const.BE999_DAFL_CLIENT:
|
||||||
@@ -74,7 +77,7 @@ class GigaFrostCameraMixin(CustomDetectorMixin):
|
|||||||
self.parent.macSouth.get(),
|
self.parent.macSouth.get(),
|
||||||
self.parent.ipSouth.get(),
|
self.parent.ipSouth.get(),
|
||||||
dest_port,
|
dest_port,
|
||||||
source_port
|
source_port,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
extend_header_table(
|
extend_header_table(
|
||||||
@@ -82,20 +85,20 @@ class GigaFrostCameraMixin(CustomDetectorMixin):
|
|||||||
self.parent.macNorth.get(),
|
self.parent.macNorth.get(),
|
||||||
self.parent.ipNorth.get(),
|
self.parent.ipNorth.get(),
|
||||||
dest_port,
|
dest_port,
|
||||||
source_port
|
source_port,
|
||||||
)
|
)
|
||||||
|
|
||||||
return udp_header_table
|
return udp_header_table
|
||||||
|
|
||||||
def on_init(self) -> None:
|
def on_init(self) -> None:
|
||||||
""" Initialize the camera, set channel values"""
|
"""Initialize the camera, set channel values"""
|
||||||
# ToDo: Not sure if it's a good idea to change camera settings upon
|
# ToDo: Not sure if it's a good idea to change camera settings upon
|
||||||
# ophyd device startup, i.e. each deviceserver restart.
|
# ophyd device startup, i.e. each deviceserver restart.
|
||||||
self._init_gigafrost()
|
self._init_gigafrost()
|
||||||
self.parent._initialized = True
|
self.parent._initialized = True
|
||||||
|
|
||||||
def _init_gigafrost(self) -> None:
|
def _init_gigafrost(self) -> None:
|
||||||
""" Initialize the camera, set channel values"""
|
"""Initialize the camera, set channel values"""
|
||||||
# Stop acquisition
|
# Stop acquisition
|
||||||
self.parent.cmdStartCamera.set(0).wait()
|
self.parent.cmdStartCamera.set(0).wait()
|
||||||
|
|
||||||
@@ -142,7 +145,7 @@ class GigaFrostCameraMixin(CustomDetectorMixin):
|
|||||||
return super().on_init()
|
return super().on_init()
|
||||||
|
|
||||||
def on_stage(self) -> None:
|
def on_stage(self) -> None:
|
||||||
""" Configuration and staging
|
"""Configuration and staging
|
||||||
|
|
||||||
In the BEC model ophyd devices must fish out their own configuration from the 'scaninfo'.
|
In the BEC model ophyd devices must fish out their own configuration from the 'scaninfo'.
|
||||||
I.e. they need to know which parameters are relevant for them at each scan.
|
I.e. they need to know which parameters are relevant for them at each scan.
|
||||||
@@ -158,25 +161,23 @@ class GigaFrostCameraMixin(CustomDetectorMixin):
|
|||||||
logger.warning(
|
logger.warning(
|
||||||
f"[{self.parent.name}] Ophyd device havent ran the initialization sequence,"
|
f"[{self.parent.name}] Ophyd device havent ran the initialization sequence,"
|
||||||
"IOC might be in unknown configuration."
|
"IOC might be in unknown configuration."
|
||||||
)
|
)
|
||||||
|
|
||||||
# Fish out our configuration from scaninfo (via explicit or generic addressing)
|
# Fish out our configuration from scaninfo (via explicit or generic addressing)
|
||||||
scanparam = self.parent.scaninfo.scan_msg.info
|
scanparam = self.parent.scaninfo.scan_msg.info
|
||||||
alias = self.parent.parent.name if self.parent.parent is not None else self.parent.name
|
|
||||||
# logger.warning(f"[{alias}] Scan parameters:\n{scanparam}")
|
|
||||||
d = {}
|
d = {}
|
||||||
if 'kwargs' in scanparam:
|
if "kwargs" in scanparam:
|
||||||
scanargs = scanparam['kwargs']
|
scanargs = scanparam["kwargs"]
|
||||||
if 'image_width' in scanargs and scanargs['image_width']!=None:
|
if "image_width" in scanargs and scanargs["image_width"] is not None:
|
||||||
d['image_width'] = scanargs['image_width']
|
d["image_width"] = scanargs["image_width"]
|
||||||
if 'image_height' in scanargs and scanargs['image_height']!=None:
|
if "image_height" in scanargs and scanargs["image_height"] is not None:
|
||||||
d['image_height'] = scanargs['image_height']
|
d["image_height"] = scanargs["image_height"]
|
||||||
if 'exp_time' in scanargs and scanargs['exp_time']!=None:
|
if "exp_time" in scanargs and scanargs["exp_time"] is not None:
|
||||||
d['exposure_time_ms'] = scanargs['exp_time']
|
d["exposure_time_ms"] = scanargs["exp_time"]
|
||||||
if 'exp_burst' in scanargs and scanargs['exp_burst']!=None:
|
if "exp_burst" in scanargs and scanargs["exp_burst"] is not None:
|
||||||
d['exposure_num_burst'] = scanargs['exp_burst']
|
d["exposure_num_burst"] = scanargs["exp_burst"]
|
||||||
if 'acq_mode' in scanargs and scanargs['acq_mode']!=None:
|
if "acq_mode" in scanargs and scanargs["acq_mode"] is not None:
|
||||||
d['acq_mode'] = scanargs['acq_mode']
|
d["acq_mode"] = scanargs["acq_mode"]
|
||||||
# elif self.parent.scaninfo.scan_type == "step":
|
# elif self.parent.scaninfo.scan_type == "step":
|
||||||
# d['acq_mode'] = "default"
|
# d['acq_mode'] = "default"
|
||||||
|
|
||||||
@@ -217,13 +218,17 @@ class GigaFrostCameraMixin(CustomDetectorMixin):
|
|||||||
Specify actions to be executed upon receiving trigger signal.
|
Specify actions to be executed upon receiving trigger signal.
|
||||||
Return a DeviceStatus object or None
|
Return a DeviceStatus object or None
|
||||||
"""
|
"""
|
||||||
if self.parent.infoBusyFlag.get() in (0, 'IDLE'):
|
if self.parent.infoBusyFlag.get() in (0, "IDLE"):
|
||||||
raise RuntimeError('GigaFrost must be running before triggering')
|
raise RuntimeError("GigaFrost must be running before triggering")
|
||||||
|
|
||||||
logger.warning(f"[{self.parent.name}] SW triggering gigafrost")
|
logger.warning(f"[{self.parent.name}] SW triggering gigafrost")
|
||||||
|
|
||||||
# Soft triggering based on operation mode
|
# Soft triggering based on operation mode
|
||||||
if self.parent.autoSoftEnable.get() and self.parent.trigger_mode == 'auto' and self.parent.enable_mode == 'soft':
|
if (
|
||||||
|
self.parent.autoSoftEnable.get()
|
||||||
|
and self.parent.trigger_mode == "auto"
|
||||||
|
and self.parent.enable_mode == "soft"
|
||||||
|
):
|
||||||
# BEC teststand operation mode: posedge of SoftEnable if Started
|
# BEC teststand operation mode: posedge of SoftEnable if Started
|
||||||
self.parent.cmdSoftEnable.set(0).wait()
|
self.parent.cmdSoftEnable.set(0).wait()
|
||||||
self.parent.cmdSoftEnable.set(1).wait()
|
self.parent.cmdSoftEnable.set(1).wait()
|
||||||
@@ -254,6 +259,7 @@ class GigaFrostCamera(PSIDetectorBase):
|
|||||||
----------
|
----------
|
||||||
FRAMERATE : Ignored in soft trigger mode, period becomes 2xExposure time
|
FRAMERATE : Ignored in soft trigger mode, period becomes 2xExposure time
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
|
|
||||||
custom_prepare_cls = GigaFrostCameraMixin
|
custom_prepare_cls = GigaFrostCameraMixin
|
||||||
@@ -267,12 +273,13 @@ class GigaFrostCamera(PSIDetectorBase):
|
|||||||
cmdSetParam = Component(EpicsSignal, "SET_PARAM.PROC", put_complete=True, kind=Kind.omitted)
|
cmdSetParam = Component(EpicsSignal, "SET_PARAM.PROC", put_complete=True, kind=Kind.omitted)
|
||||||
cfgAcqMode = Component(EpicsSignal, "ACQMODE", put_complete=True, kind=Kind.config)
|
cfgAcqMode = Component(EpicsSignal, "ACQMODE", put_complete=True, kind=Kind.config)
|
||||||
|
|
||||||
array_size = DynamicDeviceComponent({
|
array_size = DynamicDeviceComponent(
|
||||||
"array_size_x": (EpicsSignalRO, "ROIX", {'auto_monitor': True}),
|
{
|
||||||
"array_size_y": (EpicsSignalRO, "ROIY", {'auto_monitor': True}),
|
"array_size_x": (EpicsSignalRO, "ROIX", {"auto_monitor": True}),
|
||||||
}, doc="Size of the array in the XY dimensions")
|
"array_size_y": (EpicsSignalRO, "ROIY", {"auto_monitor": True}),
|
||||||
|
},
|
||||||
|
doc="Size of the array in the XY dimensions",
|
||||||
|
)
|
||||||
|
|
||||||
# UDP header
|
# UDP header
|
||||||
cfgUdpNumPorts = Component(EpicsSignal, "PORTS", put_complete=True, kind=Kind.config)
|
cfgUdpNumPorts = Component(EpicsSignal, "PORTS", put_complete=True, kind=Kind.config)
|
||||||
@@ -282,24 +289,26 @@ class GigaFrostCamera(PSIDetectorBase):
|
|||||||
|
|
||||||
# Standard camera configs
|
# Standard camera configs
|
||||||
cfgExposure = Component(
|
cfgExposure = Component(
|
||||||
EpicsSignal, "EXPOSURE", put_complete=True, auto_monitor=True, kind=Kind.config)
|
EpicsSignal, "EXPOSURE", put_complete=True, auto_monitor=True, kind=Kind.config
|
||||||
|
)
|
||||||
cfgFramerate = Component(
|
cfgFramerate = Component(
|
||||||
EpicsSignal, "FRAMERATE", put_complete=True, auto_monitor=True, kind=Kind.config)
|
EpicsSignal, "FRAMERATE", put_complete=True, auto_monitor=True, kind=Kind.config
|
||||||
cfgRoiX = Component(
|
)
|
||||||
EpicsSignal, "ROIX", put_complete=True, auto_monitor=True, kind=Kind.config)
|
cfgRoiX = Component(EpicsSignal, "ROIX", put_complete=True, auto_monitor=True, kind=Kind.config)
|
||||||
cfgRoiY = Component(
|
cfgRoiY = Component(EpicsSignal, "ROIY", put_complete=True, auto_monitor=True, kind=Kind.config)
|
||||||
EpicsSignal, "ROIY", put_complete=True, auto_monitor=True, kind=Kind.config)
|
|
||||||
cfgScanId = Component(
|
cfgScanId = Component(
|
||||||
EpicsSignal, "SCAN_ID", put_complete=True, auto_monitor=True, kind=Kind.config)
|
EpicsSignal, "SCAN_ID", put_complete=True, auto_monitor=True, kind=Kind.config
|
||||||
|
)
|
||||||
cfgCntNum = Component(
|
cfgCntNum = Component(
|
||||||
EpicsSignal, "CNT_NUM", put_complete=True, auto_monitor=True, kind=Kind.config)
|
EpicsSignal, "CNT_NUM", put_complete=True, auto_monitor=True, kind=Kind.config
|
||||||
|
)
|
||||||
cfgCorrMode = Component(
|
cfgCorrMode = Component(
|
||||||
EpicsSignal, "CORR_MODE", put_complete=True, auto_monitor=True, kind=Kind.config)
|
EpicsSignal, "CORR_MODE", put_complete=True, auto_monitor=True, kind=Kind.config
|
||||||
|
)
|
||||||
|
|
||||||
# Software signals
|
# Software signals
|
||||||
cmdSoftEnable = Component(EpicsSignal, "SOFT_ENABLE", put_complete=True)
|
cmdSoftEnable = Component(EpicsSignal, "SOFT_ENABLE", put_complete=True)
|
||||||
cmdSoftTrigger = Component(
|
cmdSoftTrigger = Component(EpicsSignal, "SOFT_TRIG.PROC", put_complete=True, kind=Kind.omitted)
|
||||||
EpicsSignal, "SOFT_TRIG.PROC", put_complete=True, kind=Kind.omitted)
|
|
||||||
cmdSoftExposure = Component(EpicsSignal, "SOFT_EXP", put_complete=True)
|
cmdSoftExposure = Component(EpicsSignal, "SOFT_EXP", put_complete=True)
|
||||||
cfgAcqMode = Component(EpicsSignal, "ACQMODE", put_complete=True, kind=Kind.config)
|
cfgAcqMode = Component(EpicsSignal, "ACQMODE", put_complete=True, kind=Kind.config)
|
||||||
|
|
||||||
@@ -403,11 +412,7 @@ class GigaFrostCamera(PSIDetectorBase):
|
|||||||
kind=Kind.config,
|
kind=Kind.config,
|
||||||
)
|
)
|
||||||
cfgCntEndBit = Component(
|
cfgCntEndBit = Component(
|
||||||
EpicsSignal,
|
EpicsSignal, "CNT_ENDBIT_RBV", write_pv="CNT_ENDBIT", put_complete=True, kind=Kind.config
|
||||||
"CNT_ENDBIT_RBV",
|
|
||||||
write_pv="CNT_ENDBIT",
|
|
||||||
put_complete=True,
|
|
||||||
kind=Kind.config
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Line swap selection
|
# Line swap selection
|
||||||
@@ -457,32 +462,41 @@ class GigaFrostCamera(PSIDetectorBase):
|
|||||||
):
|
):
|
||||||
# Ugly hack to pass values before on_init()
|
# Ugly hack to pass values before on_init()
|
||||||
self._signals_to_be_set = {}
|
self._signals_to_be_set = {}
|
||||||
self._signals_to_be_set['auto_soft_enable'] = auto_soft_enable
|
self._signals_to_be_set["auto_soft_enable"] = auto_soft_enable
|
||||||
self._signals_to_be_set['backend_url'] = backend_url
|
self._signals_to_be_set["backend_url"] = backend_url
|
||||||
|
|
||||||
# super() will call the mixin class
|
# super() will call the mixin class
|
||||||
super().__init__(prefix=prefix, name=name, kind=kind, read_attrs=read_attrs, configuration_attrs=configuration_attrs, parent=parent, device_manager=device_manager, **kwargs)
|
super().__init__(
|
||||||
|
prefix=prefix,
|
||||||
|
name=name,
|
||||||
|
kind=kind,
|
||||||
|
read_attrs=read_attrs,
|
||||||
|
configuration_attrs=configuration_attrs,
|
||||||
|
parent=parent,
|
||||||
|
device_manager=device_manager,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
def _init(self):
|
def _init(self):
|
||||||
"""Ugly hack: values must be set before on_init() is called"""
|
"""Ugly hack: values must be set before on_init() is called"""
|
||||||
# Additional parameters
|
# Additional parameters
|
||||||
self.autoSoftEnable._metadata["write_access"] = False
|
self.autoSoftEnable._metadata["write_access"] = False
|
||||||
self.backendUrl._metadata["write_access"] = False
|
self.backendUrl._metadata["write_access"] = False
|
||||||
self.autoSoftEnable.put(self._signals_to_be_set['auto_soft_enable'], force=True)
|
self.autoSoftEnable.put(self._signals_to_be_set["auto_soft_enable"], force=True)
|
||||||
self.backendUrl.put(self._signals_to_be_set['backend_url'], force=True)
|
self.backendUrl.put(self._signals_to_be_set["backend_url"], force=True)
|
||||||
return super()._init()
|
return super()._init()
|
||||||
|
|
||||||
def initialize(self):
|
def initialize(self):
|
||||||
""" Initialization in separate command"""
|
"""Initialization in separate command"""
|
||||||
self.custom_prepare._init_gigafrost()
|
self.custom_prepare._init_gigafrost()
|
||||||
self._initialized = True
|
self._initialized = True
|
||||||
|
|
||||||
def trigger(self) -> DeviceStatus:
|
def trigger(self) -> DeviceStatus:
|
||||||
""" Sends a software trigger to GigaFrost"""
|
"""Sends a software trigger to GigaFrost"""
|
||||||
super().trigger()
|
super().trigger()
|
||||||
|
|
||||||
# There's no status readback from the camera, so we just wait
|
# There's no status readback from the camera, so we just wait
|
||||||
sleep_time = self.cfgExposure.value*self.cfgCntNum.value*0.001+0.2
|
sleep_time = self.cfgExposure.value * self.cfgCntNum.value * 0.001 + 0.2
|
||||||
sleep(sleep_time)
|
sleep(sleep_time)
|
||||||
return DeviceStatus(self, done=True, success=True, settle_time=sleep_time)
|
return DeviceStatus(self, done=True, success=True, settle_time=sleep_time)
|
||||||
|
|
||||||
@@ -527,40 +541,40 @@ class GigaFrostCamera(PSIDetectorBase):
|
|||||||
# If Bluesky style configure
|
# If Bluesky style configure
|
||||||
if d is not None:
|
if d is not None:
|
||||||
# Commonly changed settings
|
# Commonly changed settings
|
||||||
if 'exposure_num_burst' in d:
|
if "exposure_num_burst" in d:
|
||||||
self.cfgCntNum.set(d['exposure_num_burst']).wait()
|
self.cfgCntNum.set(d["exposure_num_burst"]).wait()
|
||||||
if 'exposure_time_ms' in d:
|
if "exposure_time_ms" in d:
|
||||||
self.cfgExposure.set(d['exposure_time_ms']).wait()
|
self.cfgExposure.set(d["exposure_time_ms"]).wait()
|
||||||
if 'exposure_period_ms' in d:
|
if "exposure_period_ms" in d:
|
||||||
self.cfgFramerate.set(d['exposure_period_ms']).wait()
|
self.cfgFramerate.set(d["exposure_period_ms"]).wait()
|
||||||
if 'image_width' in d:
|
if "image_width" in d:
|
||||||
if d['image_width']%48 !=0:
|
if d["image_width"] % 48 != 0:
|
||||||
raise RuntimeError(f"[{self.name}] image_width must be divisible by 48")
|
raise RuntimeError(f"[{self.name}] image_width must be divisible by 48")
|
||||||
self.cfgRoiX.set(d['image_width']).wait()
|
self.cfgRoiX.set(d["image_width"]).wait()
|
||||||
if 'image_height' in d:
|
if "image_height" in d:
|
||||||
if d['image_height']%16 !=0:
|
if d["image_height"] % 16 != 0:
|
||||||
raise RuntimeError(f"[{self.name}] image_height must be divisible by 16")
|
raise RuntimeError(f"[{self.name}] image_height must be divisible by 16")
|
||||||
self.cfgRoiY.set(d['image_height']).wait()
|
self.cfgRoiY.set(d["image_height"]).wait()
|
||||||
# Dont change these
|
# Dont change these
|
||||||
scanid = d.get('scanid', 0)
|
scanid = d.get("scanid", 0)
|
||||||
correction_mode = d.get('correction_mode', 5)
|
correction_mode = d.get("correction_mode", 5)
|
||||||
self.cfgScanId.set(scanid).wait()
|
self.cfgScanId.set(scanid).wait()
|
||||||
self.cfgCorrMode.set(correction_mode).wait()
|
self.cfgCorrMode.set(correction_mode).wait()
|
||||||
|
|
||||||
if 'acq_mode' in d:
|
if "acq_mode" in d:
|
||||||
self.set_acquisition_mode(d['acq_mode'])
|
self.set_acquisition_mode(d["acq_mode"])
|
||||||
|
|
||||||
# Commit parameters
|
# Commit parameters
|
||||||
self.cmdSetParam.set(1).wait()
|
self.cmdSetParam.set(1).wait()
|
||||||
|
|
||||||
def bluestage(self):
|
def bluestage(self):
|
||||||
""" Bluesky style stage"""
|
"""Bluesky style stage"""
|
||||||
# Switch to acquiring
|
# Switch to acquiring
|
||||||
self.cmdStartCamera.set(1).wait()
|
self.cmdStartCamera.set(1).wait()
|
||||||
|
|
||||||
def set_acquisition_mode(self, acq_mode):
|
def set_acquisition_mode(self, acq_mode):
|
||||||
""" Set acquisition mode
|
"""Set acquisition mode
|
||||||
|
|
||||||
Utility function to quickly select between pre-configured and tested
|
Utility function to quickly select between pre-configured and tested
|
||||||
acquisition modes.
|
acquisition modes.
|
||||||
|
|
||||||
@@ -577,7 +591,7 @@ class GigaFrostCamera(PSIDetectorBase):
|
|||||||
self.cfgEnableScheme.set(0).wait()
|
self.cfgEnableScheme.set(0).wait()
|
||||||
|
|
||||||
# Set modes
|
# Set modes
|
||||||
#self.cmdSoftEnable.set(0).wait()
|
# self.cmdSoftEnable.set(0).wait()
|
||||||
self.enable_mode = "soft"
|
self.enable_mode = "soft"
|
||||||
self.trigger_mode = "auto"
|
self.trigger_mode = "auto"
|
||||||
self.exposure_mode = "timer"
|
self.exposure_mode = "timer"
|
||||||
@@ -834,11 +848,11 @@ class GigaFrostCamera(PSIDetectorBase):
|
|||||||
The GigaFRoST enable mode. Valid arguments are:
|
The GigaFRoST enable mode. Valid arguments are:
|
||||||
|
|
||||||
* 'soft':
|
* 'soft':
|
||||||
The GigaFRoST enable signal is supplied through a software
|
The GigaFRoST enable signal is supplied through a software
|
||||||
signal
|
signal
|
||||||
* 'external':
|
* 'external':
|
||||||
The GigaFRoST enable signal is supplied through an external TTL
|
The GigaFRoST enable signal is supplied through an external TTL
|
||||||
gating signal from the rotaiton stage or some other control
|
gating signal from the rotaiton stage or some other control
|
||||||
unit
|
unit
|
||||||
* 'soft+ext':
|
* 'soft+ext':
|
||||||
The GigaFRoST enable signal can be supplied either via the
|
The GigaFRoST enable signal can be supplied either via the
|
||||||
@@ -851,9 +865,7 @@ class GigaFrostCamera(PSIDetectorBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
if mode not in const.gf_valid_enable_modes:
|
if mode not in const.gf_valid_enable_modes:
|
||||||
raise ValueError(
|
raise ValueError("Invalid enable mode! Valid modes are:\n{const.gf_valid_enable_modes}")
|
||||||
"Invalid enable mode! Valid modes are:\n{const.gf_valid_enable_modes}"
|
|
||||||
)
|
|
||||||
|
|
||||||
if mode == "soft":
|
if mode == "soft":
|
||||||
self.cfgEnableExt.set(0).wait()
|
self.cfgEnableExt.set(0).wait()
|
||||||
|
|||||||
@@ -6,22 +6,19 @@ Created on Thu Jun 27 17:28:43 2024
|
|||||||
|
|
||||||
@author: mohacsi_i
|
@author: mohacsi_i
|
||||||
"""
|
"""
|
||||||
import json
|
|
||||||
import enum
|
|
||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import zmq
|
import zmq
|
||||||
import numpy as np
|
from ophyd import Device, Signal, Component, Kind
|
||||||
from ophyd import Device, Signal, Component, Kind, DeviceStatus
|
|
||||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
||||||
CustomDetectorMixin,
|
CustomDetectorMixin,
|
||||||
PSIDetectorBase,
|
PSIDetectorBase,
|
||||||
)
|
)
|
||||||
|
|
||||||
from bec_lib import bec_logger
|
from bec_lib import bec_logger
|
||||||
logger = bec_logger.logger
|
|
||||||
ZMQ_TOPIC_FILTER = b''
|
|
||||||
|
|
||||||
|
logger = bec_logger.logger
|
||||||
|
ZMQ_TOPIC_FILTER = b""
|
||||||
|
|
||||||
|
|
||||||
class PcoTestConsumerMixin(CustomDetectorMixin):
|
class PcoTestConsumerMixin(CustomDetectorMixin):
|
||||||
@@ -29,6 +26,7 @@ class PcoTestConsumerMixin(CustomDetectorMixin):
|
|||||||
|
|
||||||
Parent class: CustomDetectorMixin
|
Parent class: CustomDetectorMixin
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=protected-access
|
||||||
def on_stage(self):
|
def on_stage(self):
|
||||||
"""Start listening for preview data stream"""
|
"""Start listening for preview data stream"""
|
||||||
if self.parent._mon is not None:
|
if self.parent._mon is not None:
|
||||||
@@ -72,29 +70,27 @@ class PcoTestConsumerMixin(CustomDetectorMixin):
|
|||||||
t_elapsed = t_curr - t_last
|
t_elapsed = t_curr - t_last
|
||||||
if t_elapsed < self.parent.throttle.get():
|
if t_elapsed < self.parent.throttle.get():
|
||||||
continue
|
continue
|
||||||
"""
|
# # Unpack the Array V1 reply to metadata and array data
|
||||||
# Unpack the Array V1 reply to metadata and array data
|
# meta, data = r
|
||||||
meta, data = r
|
# print(meta)
|
||||||
print(meta)
|
|
||||||
|
|
||||||
# Update image and update subscribers
|
# # Update image and update subscribers
|
||||||
header = json.loads(meta)
|
# header = json.loads(meta)
|
||||||
if header["type"] == "uint16":
|
# if header["type"] == "uint16":
|
||||||
image = np.frombuffer(data, dtype=np.uint16)
|
# image = np.frombuffer(data, dtype=np.uint16)
|
||||||
if image.size != np.prod(header['shape']):
|
# if image.size != np.prod(header['shape']):
|
||||||
err = f"Unexpected array size of {image.size} for header: {header}"
|
# err = f"Unexpected array size of {image.size} for header: {header}"
|
||||||
raise ValueError(err)
|
# raise ValueError(err)
|
||||||
image = image.reshape(header['shape'])
|
# image = image.reshape(header['shape'])
|
||||||
|
|
||||||
# Update image and update subscribers
|
# # Update image and update subscribers
|
||||||
self.parent.frame.put(header['frame'], force=True)
|
# self.parent.frame.put(header['frame'], force=True)
|
||||||
self.parent.image_shape.put(header['shape'], force=True)
|
# self.parent.image_shape.put(header['shape'], force=True)
|
||||||
self.parent.image.put(image, force=True)
|
# self.parent.image.put(image, force=True)
|
||||||
self.parent._last_image = image
|
# self.parent._last_image = image
|
||||||
self.parent._run_subs(sub_type=self.parent.SUB_MONITOR, value=image)
|
# self.parent._run_subs(sub_type=self.parent.SUB_MONITOR, value=image)
|
||||||
"""
|
|
||||||
t_last = t_curr
|
t_last = t_curr
|
||||||
#logger.info(
|
# logger.info(
|
||||||
# f"[{self.parent.name}] Updated frame {header['frame']}\t"
|
# f"[{self.parent.name}] Updated frame {header['frame']}\t"
|
||||||
# f"Shape: {header['shape']}\tMean: {np.mean(image):.3f}"
|
# f"Shape: {header['shape']}\tMean: {np.mean(image):.3f}"
|
||||||
# )
|
# )
|
||||||
@@ -110,7 +106,7 @@ class PcoTestConsumerMixin(CustomDetectorMixin):
|
|||||||
finally:
|
finally:
|
||||||
try:
|
try:
|
||||||
self.parent._socket.disconnect()
|
self.parent._socket.disconnect()
|
||||||
except:
|
except RuntimeError:
|
||||||
pass
|
pass
|
||||||
self.parent._mon = None
|
self.parent._mon = None
|
||||||
logger.info(f"[{self.parent.name}]\tDetaching monitor")
|
logger.info(f"[{self.parent.name}]\tDetaching monitor")
|
||||||
@@ -126,6 +122,7 @@ class PcoTestConsumer(PSIDetectorBase):
|
|||||||
You can add a preview widget to the dock by:
|
You can add a preview widget to the dock by:
|
||||||
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 = ["get_last_image"]
|
USER_ACCESS = ["get_last_image"]
|
||||||
SUB_MONITOR = "device_monitor_2d"
|
SUB_MONITOR = "device_monitor_2d"
|
||||||
@@ -171,8 +168,7 @@ class PcoTestConsumer(PSIDetectorBase):
|
|||||||
self._socket.connect(self.url.get())
|
self._socket.connect(self.url.get())
|
||||||
|
|
||||||
def disconnect(self):
|
def disconnect(self):
|
||||||
"""Disconnect
|
"""Disconnect"""
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
if self._socket is not None:
|
if self._socket is not None:
|
||||||
self._socket.disconnect(self.url.get())
|
self._socket.disconnect(self.url.get())
|
||||||
@@ -181,7 +177,6 @@ class PcoTestConsumer(PSIDetectorBase):
|
|||||||
finally:
|
finally:
|
||||||
self._socket = None
|
self._socket = None
|
||||||
|
|
||||||
|
|
||||||
def get_image(self):
|
def get_image(self):
|
||||||
return self._last_image
|
return self._last_image
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,12 @@ Created on Wed Dec 6 11:33:54 2023
|
|||||||
|
|
||||||
@author: mohacsi_i
|
@author: mohacsi_i
|
||||||
"""
|
"""
|
||||||
|
import time
|
||||||
from ophyd import Component, EpicsSignal, EpicsSignalRO, Kind
|
from ophyd import Component, EpicsSignal, EpicsSignalRO, Kind
|
||||||
from ophyd.status import SubscriptionStatus, DeviceStatus
|
from ophyd.status import SubscriptionStatus, DeviceStatus
|
||||||
import time
|
from ophyd_devices import BECDeviceBase
|
||||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase as PSIDeviceBase
|
|
||||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
||||||
CustomDetectorMixin as CustomDeviceMixin,
|
CustomDetectorMixin as CustomPrepare,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -23,12 +22,12 @@ except ModuleNotFoundError:
|
|||||||
logger = logging.getLogger("PcoEdgeCam")
|
logger = logging.getLogger("PcoEdgeCam")
|
||||||
|
|
||||||
|
|
||||||
class PcoEdgeCameraMixin(CustomDeviceMixin):
|
class PcoEdgeCameraMixin(CustomPrepare):
|
||||||
"""Mixin class to setup the Helge camera bae class.
|
"""Mixin class to setup the Helge camera bae class.
|
||||||
|
|
||||||
This class will be called by the custom_prepare_cls attribute of the detector class.
|
This class will be called by the custom_prepare_cls attribute of the detector class.
|
||||||
"""
|
"""
|
||||||
|
# pylint: disable=protected-access
|
||||||
def on_stage(self) -> None:
|
def on_stage(self) -> None:
|
||||||
"""Configure and arm PCO.Edge camera for acquisition"""
|
"""Configure and arm PCO.Edge camera for acquisition"""
|
||||||
|
|
||||||
@@ -91,7 +90,6 @@ class PcoEdgeCameraMixin(CustomDeviceMixin):
|
|||||||
NOTE: Maciej confirmed that sparse data is no problem to the stdDAQ.
|
NOTE: Maciej confirmed that sparse data is no problem to the stdDAQ.
|
||||||
TODO: Optimize data transfer to launch at end and check completion at the beginning.
|
TODO: Optimize data transfer to launch at end and check completion at the beginning.
|
||||||
"""
|
"""
|
||||||
logger.warning(f"triggering PCO")
|
|
||||||
# Ensure that previous data transfer finished
|
# Ensure that previous data transfer finished
|
||||||
# def sentIt(*args, value, timestamp, **kwargs):
|
# def sentIt(*args, value, timestamp, **kwargs):
|
||||||
# return value==0
|
# return value==0
|
||||||
@@ -99,11 +97,11 @@ class PcoEdgeCameraMixin(CustomDeviceMixin):
|
|||||||
# status.wait()
|
# status.wait()
|
||||||
|
|
||||||
# Not sure if it always sends the first batch of images or the newest
|
# Not sure if it always sends the first batch of images or the newest
|
||||||
def didWeReset(*args, old_value, value, timestamp, **kwargs):
|
def wait_bufferreset(*, old_value, value, timestamp, **_):
|
||||||
return (value < old_value) or (value == 0)
|
return (value < old_value) or (value == 0)
|
||||||
|
|
||||||
self.parent.buffer_clear.set(1).wait()
|
self.parent.buffer_clear.set(1).wait()
|
||||||
status = SubscriptionStatus(self.parent.buffer_used, didWeReset, timeout=5)
|
status = SubscriptionStatus(self.parent.buffer_used, wait_bufferreset, timeout=5)
|
||||||
status.wait()
|
status.wait()
|
||||||
|
|
||||||
t_expected = (
|
t_expected = (
|
||||||
@@ -111,13 +109,13 @@ class PcoEdgeCameraMixin(CustomDeviceMixin):
|
|||||||
) * self.parent.file_savestop.get()
|
) * self.parent.file_savestop.get()
|
||||||
|
|
||||||
# Wait until the buffer fills up with enough images
|
# Wait until the buffer fills up with enough images
|
||||||
def areWeDoneYet(*args, old_value, value, timestamp, **kwargs):
|
def wait_acquisition(*, value, timestamp, **_):
|
||||||
num_target = self.parent.file_savestop.get()
|
num_target = self.parent.file_savestop.get()
|
||||||
# logger.warning(f"{value} of {num_target}")
|
# logger.warning(f"{value} of {num_target}")
|
||||||
return bool(value >= num_target)
|
return bool(value >= num_target)
|
||||||
|
max_wait = max(5, 5 * t_expected)
|
||||||
status = SubscriptionStatus(
|
status = SubscriptionStatus(
|
||||||
self.parent.buffer_used, areWeDoneYet, timeout=max(5, 5 * t_expected), settle_time=0.2
|
self.parent.buffer_used, wait_acquisition, timeout=max_wait, settle_time=0.2
|
||||||
)
|
)
|
||||||
status.wait()
|
status.wait()
|
||||||
|
|
||||||
@@ -130,19 +128,18 @@ class PcoEdgeCameraMixin(CustomDeviceMixin):
|
|||||||
# against values from the previous cycle, i.e. pass automatically.
|
# against values from the previous cycle, i.e. pass automatically.
|
||||||
t_start = time.time()
|
t_start = time.time()
|
||||||
|
|
||||||
def haveWeSentIt(*args, old_value, value, timestamp, **kwargs):
|
def wait_sending(*args, old_value, value, timestamp, **kwargs):
|
||||||
t_elapsed = timestamp - t_start
|
t_elapsed = timestamp - t_start
|
||||||
# logger.warning(f"{old_value}\t{value}\t{t_elapsed}")
|
# logger.warning(f"{old_value}\t{value}\t{t_elapsed}")
|
||||||
return old_value == 1 and value == 0 and t_elapsed > 0
|
return old_value == 1 and value == 0 and t_elapsed > 0
|
||||||
|
|
||||||
status = SubscriptionStatus(
|
status = SubscriptionStatus(
|
||||||
self.parent.file_savebusy, haveWeSentIt, timeout=120, settle_time=0.2
|
self.parent.file_savebusy, wait_sending, timeout=120, settle_time=0.2
|
||||||
)
|
)
|
||||||
status.wait()
|
status.wait()
|
||||||
logger.warning(f"done PCO")
|
|
||||||
|
|
||||||
|
|
||||||
class HelgeCameraBase(PSIDeviceBase):
|
class HelgeCameraBase(BECDeviceBase):
|
||||||
"""Ophyd baseclass for Helge camera IOCs
|
"""Ophyd baseclass for Helge camera IOCs
|
||||||
|
|
||||||
This class provides wrappers for Helge's camera IOCs around SwissFEL and
|
This class provides wrappers for Helge's camera IOCs around SwissFEL and
|
||||||
@@ -161,19 +158,21 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
|
|
||||||
|
|
||||||
UPDATE: Data sending operation modes
|
UPDATE: Data sending operation modes
|
||||||
Switch to ZMQ streaming by setting FILEFORMAT to ZEROMQ, set SAVESTART and SAVESTOP to select a ROI of images and start file transfer with FTRANSFER.
|
- Switch to ZMQ streaming by setting FILEFORMAT to ZEROMQ
|
||||||
The ZMQ connection streams out the data in PUSH-PULL mode, i.e. it needs incoming connection.
|
- Set SAVESTART and SAVESTOP to select a ROI of image indices
|
||||||
|
- Start file transfer with FTRANSFER.
|
||||||
|
The ZMQ connection operates in PUSH-PULL mode, i.e. it needs incoming connection.
|
||||||
|
|
||||||
STOREMODE sets the acquisition mode:
|
STOREMODE sets the acquisition mode:
|
||||||
if STOREMODE == Recorder
|
if STOREMODE == Recorder
|
||||||
Fills up the buffer with images and SAVESTART and SAVESTOP selects a ROI of images to be streamed
|
Fills up the buffer with images. Here SAVESTART and SAVESTOP selects a ROI
|
||||||
|
of image indices to be streamed out (i.e. maximum buffer_size number of images)
|
||||||
|
|
||||||
if STOREMODE == FIFO buffer
|
if STOREMODE == FIFO buffer
|
||||||
Continously streams out data using the buffer as a FIFO queue and SAVESTART and SAVESTOP selects a ROI of images to be streamed continously (i.e. a large SAVESTOP streams indefinitely)
|
Continously streams out data using the buffer as a FIFO queue.
|
||||||
|
Here SAVESTART and SAVESTOP selects a ROI of image indices to be streamed continously
|
||||||
Note that in FIFO mode buffer reads are destructive, to prevent this, we don't have EPICS preview
|
(i.e. a large SAVESTOP streams indefinitely). Note that in FIFO mode buffer reads are
|
||||||
|
destructive. to prevent this, we don't have EPICS preview
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
@@ -274,7 +273,7 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
|
|
||||||
@state.setter
|
@state.setter
|
||||||
def state(self):
|
def state(self):
|
||||||
raise ReadOnlyError("State is a ReadOnly property")
|
raise RuntimeError("State is a ReadOnly property")
|
||||||
|
|
||||||
def configure(self, d: dict = {}) -> tuple:
|
def configure(self, d: dict = {}) -> tuple:
|
||||||
"""Configure the base Helge camera device
|
"""Configure the base Helge camera device
|
||||||
@@ -323,11 +322,11 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
# So we need a 'negedge' on SET_PARAM
|
# So we need a 'negedge' on SET_PARAM
|
||||||
self.camSetParam.set(1).wait()
|
self.camSetParam.set(1).wait()
|
||||||
|
|
||||||
def fallingEdge(*args, old_value, value, timestamp, **kwargs):
|
def negedge(*, old_value, value, timestamp, **_):
|
||||||
return bool(old_value and not value)
|
return bool(old_value and not value)
|
||||||
|
|
||||||
# Subscribe and wait for update
|
# Subscribe and wait for update
|
||||||
status = SubscriptionStatus(self.camSetParam, fallingEdge, timeout=5, settle_time=0.5)
|
status = SubscriptionStatus(self.camSetParam, negedge, timeout=5, settle_time=0.5)
|
||||||
status.wait()
|
status.wait()
|
||||||
|
|
||||||
def bluestage(self):
|
def bluestage(self):
|
||||||
@@ -341,24 +340,23 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
self.bufferStoreMode.get() in ("Recorder", 0)
|
self.bufferStoreMode.get() in ("Recorder", 0)
|
||||||
and self.file_savestop.get() > self.buffer_size.get()
|
and self.file_savestop.get() > self.buffer_size.get()
|
||||||
):
|
):
|
||||||
self.logger.warning(
|
logger.warning(
|
||||||
"You're about to send some empty images, {self.file_savestop.get()} is above buffer size"
|
f"You'll send empty images, {self.file_savestop.get()} is above buffer size"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Start the acquisition (this sets parameers and starts acquisition)
|
# Start the acquisition (this sets parameers and starts acquisition)
|
||||||
self.camStatusCmd.set("Running").wait()
|
self.camStatusCmd.set("Running").wait()
|
||||||
|
|
||||||
# Subscribe and wait for update
|
# Subscribe and wait for update
|
||||||
def isRunning(*args, old_value, value, timestamp, **kwargs):
|
def is_running(*, value, timestamp, **_):
|
||||||
return bool(value == 6)
|
return bool(value == 6)
|
||||||
|
|
||||||
status = SubscriptionStatus(self.camStatusCode, isRunning, timeout=5, settle_time=0.2)
|
status = SubscriptionStatus(self.camStatusCode, is_running, timeout=5, settle_time=0.2)
|
||||||
status.wait()
|
status.wait()
|
||||||
|
|
||||||
def blueunstage(self):
|
def blueunstage(self):
|
||||||
"""Bluesky style unstage: stop the detector"""
|
"""Bluesky style unstage: stop the detector"""
|
||||||
self.camStatusCmd.set("Idle").wait()
|
self.camStatusCmd.set("Idle").wait()
|
||||||
self.custom_prepare.stop_monitor = True
|
|
||||||
|
|
||||||
# Data streaming is stopped by setting the max index to 0
|
# Data streaming is stopped by setting the max index to 0
|
||||||
# FIXME: This might interrupt data transfer
|
# FIXME: This might interrupt data transfer
|
||||||
@@ -371,13 +369,6 @@ class HelgeCameraBase(PSIDeviceBase):
|
|||||||
"""
|
"""
|
||||||
self.file_transfer.set(1).wait()
|
self.file_transfer.set(1).wait()
|
||||||
|
|
||||||
# def complete(self):
|
|
||||||
# """ Wait until the images have been sent"""
|
|
||||||
# def areWeSending(*args, value, timestamp, **kwargs):
|
|
||||||
# return not bool(value)
|
|
||||||
# status = SubscriptionStatus(self.file_savebusy, haveWeSentIt, timeout=None, settle_time=0.2)
|
|
||||||
# return status
|
|
||||||
|
|
||||||
|
|
||||||
class PcoEdge5M(HelgeCameraBase):
|
class PcoEdge5M(HelgeCameraBase):
|
||||||
"""Ophyd baseclass for PCO.Edge cameras
|
"""Ophyd baseclass for PCO.Edge cameras
|
||||||
@@ -402,7 +393,8 @@ class PcoEdge5M(HelgeCameraBase):
|
|||||||
acqMode = Component(EpicsSignalRO, "ACQMODE", auto_monitor=True, kind=Kind.config)
|
acqMode = Component(EpicsSignalRO, "ACQMODE", auto_monitor=True, kind=Kind.config)
|
||||||
acqDelay = Component(EpicsSignalRO, "DELAY", auto_monitor=True, kind=Kind.config)
|
acqDelay = Component(EpicsSignalRO, "DELAY", auto_monitor=True, kind=Kind.config)
|
||||||
acqTriggerEna = Component(EpicsSignalRO, "TRIGGER", auto_monitor=True, kind=Kind.config)
|
acqTriggerEna = Component(EpicsSignalRO, "TRIGGER", auto_monitor=True, kind=Kind.config)
|
||||||
# acqTriggerSource = Component(EpicsSignalRO, "TRIGGERSOURCE", auto_monitor=True, kind=Kind.config)
|
# acqTriggerSource = Component(
|
||||||
|
# EpicsSignalRO, "TRIGGERSOURCE", auto_monitor=True, kind=Kind.config)
|
||||||
# acqTriggerEdge = Component(EpicsSignalRO, "TRIGGEREDGE", auto_monitor=True, kind=Kind.config)
|
# acqTriggerEdge = Component(EpicsSignalRO, "TRIGGEREDGE", auto_monitor=True, kind=Kind.config)
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
@@ -465,9 +457,9 @@ class PcoEdge5M(HelgeCameraBase):
|
|||||||
self.pxRoiY_lo.set(2160 / 2 - height / 2).wait()
|
self.pxRoiY_lo.set(2160 / 2 - height / 2).wait()
|
||||||
self.pxRoiY_hi.set(2160 / 2 + height / 2).wait()
|
self.pxRoiY_hi.set(2160 / 2 + height / 2).wait()
|
||||||
if "image_binx" in d and d["image_binx"] is not None:
|
if "image_binx" in d and d["image_binx"] is not None:
|
||||||
self.pxBinX.set(d["image_binx"]).wait()
|
self.bin_x.set(d["image_binx"]).wait()
|
||||||
if "image_biny" in d and d["image_biny"] is not None:
|
if "image_biny" in d and d["image_biny"] is not None:
|
||||||
self.pxBinY.set(d["image_biny"]).wait()
|
self.bin_y.set(d["image_biny"]).wait()
|
||||||
|
|
||||||
# Call super() to commit the changes
|
# Call super() to commit the changes
|
||||||
super().configure(d)
|
super().configure(d)
|
||||||
@@ -477,5 +469,5 @@ class PcoEdge5M(HelgeCameraBase):
|
|||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
# Drive data collection
|
# Drive data collection
|
||||||
cam = PcoEdgeBase("X02DA-CCDCAM2:", name="mcpcam")
|
cam = PcoEdge5M("X02DA-CCDCAM2:", name="mcpcam")
|
||||||
cam.wait_for_connection()
|
cam.wait_for_connection()
|
||||||
|
|||||||
@@ -12,25 +12,26 @@ from threading import Thread
|
|||||||
import requests
|
import requests
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from ophyd import Device, Signal, Component, Kind, Staged
|
from ophyd import Signal, Component, Kind
|
||||||
from ophyd.status import SubscriptionStatus
|
from ophyd.status import SubscriptionStatus
|
||||||
from ophyd.flyers import FlyerInterface
|
|
||||||
from websockets.sync.client import connect, ClientConnection
|
from websockets.sync.client import connect, ClientConnection
|
||||||
from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
|
from websockets.exceptions import ConnectionClosedOK, ConnectionClosedError
|
||||||
|
|
||||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase as PSIDeviceBase
|
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase as PSIDeviceBase
|
||||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import CustomDetectorMixin as CustomDeviceMixin
|
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
||||||
|
CustomDetectorMixin as CustomDeviceMixin,
|
||||||
|
)
|
||||||
from bec_lib import bec_logger
|
from bec_lib import bec_logger
|
||||||
from bec_lib.file_utils import FileWriter
|
|
||||||
logger = bec_logger.logger
|
logger = bec_logger.logger
|
||||||
|
|
||||||
|
|
||||||
class StdDaqMixin(CustomDeviceMixin):
|
class StdDaqMixin(CustomDeviceMixin):
|
||||||
# parent : StdDaqClient
|
# pylint: disable=protected-access
|
||||||
_mon = None
|
_mon = None
|
||||||
|
|
||||||
def on_stage(self) -> None:
|
def on_stage(self) -> None:
|
||||||
""" Configuration and staging
|
"""Configuration and staging
|
||||||
|
|
||||||
In the BEC model ophyd devices must fish out their own configuration from the 'scaninfo'.
|
In the BEC model ophyd devices must fish out their own configuration from the 'scaninfo'.
|
||||||
I.e. they need to know which parameters are relevant for them at each scan.
|
I.e. they need to know which parameters are relevant for them at each scan.
|
||||||
@@ -40,30 +41,30 @@ 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 = {}
|
||||||
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'] != None:
|
if "image_width" in scanargs and scanargs["image_width"] is not None:
|
||||||
d['image_width'] = scanargs['image_width']
|
d["image_width"] = scanargs["image_width"]
|
||||||
if 'image_height' in scanargs and scanargs['image_height'] != None:
|
if "image_height" in scanargs and scanargs["image_height"] is not None:
|
||||||
d['image_height'] = scanargs['image_height']
|
d["image_height"] = scanargs["image_height"]
|
||||||
if 'nr_writers' in scanargs and scanargs['nr_writers'] != 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 'file_path' in scanargs and scanargs['file_path']!=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()
|
||||||
print(scanargs['file_path'])
|
print(scanargs["file_path"])
|
||||||
if os.path.isdir(scanargs['file_path']):
|
if os.path.isdir(scanargs["file_path"]):
|
||||||
print("isdir")
|
print("isdir")
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
print("creating")
|
print("creating")
|
||||||
try:
|
try:
|
||||||
os.makedirs(scanargs['file_path'], 0o777)
|
os.makedirs(scanargs["file_path"], 0o777)
|
||||||
os.system('chmod -R 777 ' + scanargs['base_path'])
|
os.system("chmod -R 777 " + scanargs["base_path"])
|
||||||
except:
|
except:
|
||||||
print('Problem with creating folder')
|
print("Problem with creating folder")
|
||||||
if 'file_prefix' in scanargs and scanargs['file_prefix']!=None:
|
if "file_prefix" in scanargs and scanargs["file_prefix"] != None:
|
||||||
print(scanargs['file_prefix'])
|
print(scanargs["file_prefix"])
|
||||||
self.parent.file_prefix.set(scanargs['file_prefix']).wait()
|
self.parent.file_prefix.set(scanargs["file_prefix"]).wait()
|
||||||
|
|
||||||
if "daq_num_points" in scanargs:
|
if "daq_num_points" in scanargs:
|
||||||
d["num_points_total"] = scanargs["daq_num_points"]
|
d["num_points_total"] = scanargs["daq_num_points"]
|
||||||
@@ -71,13 +72,13 @@ class StdDaqMixin(CustomDeviceMixin):
|
|||||||
# Try to figure out number of points
|
# Try to figure out number of points
|
||||||
num_points = 1
|
num_points = 1
|
||||||
points_valid = False
|
points_valid = False
|
||||||
if "steps" in scanargs and scanargs['steps'] is not None:
|
if "steps" in scanargs and scanargs["steps"] is not None:
|
||||||
num_points *= scanargs["steps"]
|
num_points *= scanargs["steps"]
|
||||||
points_valid = True
|
points_valid = True
|
||||||
if "exp_burst" in scanargs and scanargs['exp_burst'] is not None:
|
if "exp_burst" in scanargs and scanargs["exp_burst"] is not None:
|
||||||
num_points *= scanargs["exp_burst"]
|
num_points *= scanargs["exp_burst"]
|
||||||
points_valid = True
|
points_valid = True
|
||||||
if "repeats" in scanargs and scanargs['repeats'] is not None:
|
if "repeats" in scanargs and scanargs["repeats"] is not None:
|
||||||
num_points *= scanargs["repeats"]
|
num_points *= scanargs["repeats"]
|
||||||
points_valid = True
|
points_valid = True
|
||||||
if points_valid:
|
if points_valid:
|
||||||
@@ -98,19 +99,17 @@ class StdDaqMixin(CustomDeviceMixin):
|
|||||||
self._mon.start()
|
self._mon.start()
|
||||||
|
|
||||||
def on_unstage(self):
|
def on_unstage(self):
|
||||||
""" Stop a running acquisition and close connection
|
"""Stop a running acquisition and close connection"""
|
||||||
"""
|
|
||||||
print("Creating virtual dataset")
|
print("Creating virtual dataset")
|
||||||
self.parent.create_virtual_dataset()
|
self.parent.create_virtual_dataset()
|
||||||
self.parent.blueunstage()
|
self.parent.blueunstage()
|
||||||
|
|
||||||
def on_stop(self):
|
def on_stop(self):
|
||||||
""" Stop a running acquisition and close connection
|
"""Stop a running acquisition and close connection"""
|
||||||
"""
|
|
||||||
self.parent.blueunstage()
|
self.parent.blueunstage()
|
||||||
|
|
||||||
def monitor(self) -> None:
|
def monitor(self) -> None:
|
||||||
""" Monitor status messages while connection is open. This will block the reply monitoring
|
"""Monitor status messages while connection is open. This will block the reply monitoring
|
||||||
to calling unstage() might throw. Status updates are sent every 1 seconds, but finishing
|
to calling unstage() might throw. Status updates are sent every 1 seconds, but finishing
|
||||||
acquisition means StdDAQ will close connection, so there's no idle state polling.
|
acquisition means StdDAQ will close connection, so there's no idle state polling.
|
||||||
"""
|
"""
|
||||||
@@ -144,19 +143,31 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
daq = StdDaqClient(name="daq", ws_url="ws://xbl-daq-29:8080", rest_url="http://xbl-daq-29:5000")
|
daq = StdDaqClient(name="daq", ws_url="ws://xbl-daq-29:8080", rest_url="http://xbl-daq-29:5000")
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# pylint: disable=too-many-instance-attributes
|
# pylint: disable=too-many-instance-attributes
|
||||||
custom_prepare_cls = StdDaqMixin
|
custom_prepare_cls = StdDaqMixin
|
||||||
USER_ACCESS = ["set_daq_config", "get_daq_config", "nuke", "connect", "message", "state", "bluestage", "blueunstage"]
|
USER_ACCESS = [
|
||||||
|
"set_daq_config",
|
||||||
|
"get_daq_config",
|
||||||
|
"nuke",
|
||||||
|
"connect",
|
||||||
|
"message",
|
||||||
|
"state",
|
||||||
|
"bluestage",
|
||||||
|
"blueunstage",
|
||||||
|
]
|
||||||
_wsclient = None
|
_wsclient = None
|
||||||
|
|
||||||
# Status attributes
|
# Status attributes
|
||||||
ws_url = Component(Signal, kind=Kind.config, metadata={'write_access': False})
|
ws_url = Component(Signal, kind=Kind.config, metadata={"write_access": False})
|
||||||
runstatus = Component(Signal, value="unknown", kind=Kind.normal, metadata={'write_access': False})
|
runstatus = Component(
|
||||||
|
Signal, value="unknown", kind=Kind.normal, metadata={"write_access": False}
|
||||||
|
)
|
||||||
num_images = Component(Signal, value=10000, kind=Kind.config)
|
num_images = Component(Signal, value=10000, kind=Kind.config)
|
||||||
file_path = Component(Signal, value="/gpfs/test/test-beamline", kind=Kind.config)
|
file_path = Component(Signal, value="/gpfs/test/test-beamline", kind=Kind.config)
|
||||||
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})
|
||||||
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)
|
||||||
@@ -176,10 +187,19 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
device_manager=None,
|
device_manager=None,
|
||||||
ws_url: str = "ws://localhost:8080",
|
ws_url: str = "ws://localhost:8080",
|
||||||
rest_url: str = "http://localhost:5000",
|
rest_url: str = "http://localhost:5000",
|
||||||
data_source_name = None,
|
data_source_name=None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(prefix=prefix, name=name, kind=kind, read_attrs=read_attrs, configuration_attrs=configuration_attrs, parent=parent, device_manager=device_manager, **kwargs)
|
super().__init__(
|
||||||
|
prefix=prefix,
|
||||||
|
name=name,
|
||||||
|
kind=kind,
|
||||||
|
read_attrs=read_attrs,
|
||||||
|
configuration_attrs=configuration_attrs,
|
||||||
|
parent=parent,
|
||||||
|
device_manager=device_manager,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
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.data_source_name = data_source_name
|
||||||
@@ -264,47 +284,49 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
# Configuration parameters
|
# Configuration parameters
|
||||||
if 'image_width' in d and d['image_width']!=None:
|
if "image_width" in d and d["image_width"] != None:
|
||||||
self.cfg_pixel_width.set(d['image_width']).wait()
|
self.cfg_pixel_width.set(d["image_width"]).wait()
|
||||||
if 'image_height' in d and d['image_height']!=None:
|
if "image_height" in d and d["image_height"] != None:
|
||||||
self.cfg_pixel_height.set(d['image_height']).wait()
|
self.cfg_pixel_height.set(d["image_height"]).wait()
|
||||||
if 'bit_depth' in d:
|
if "bit_depth" in d:
|
||||||
self.cfg_bit_depth.set(d['bit_depth']).wait()
|
self.cfg_bit_depth.set(d["bit_depth"]).wait()
|
||||||
if 'nr_writers' in d and d['nr_writers']!=None:
|
if "nr_writers" in d and d["nr_writers"] != None:
|
||||||
self.cfg_nr_writers.set(d['nr_writers']).wait()
|
self.cfg_nr_writers.set(d["nr_writers"]).wait()
|
||||||
# Run parameters
|
# Run parameters
|
||||||
if 'num_points_total' in d:
|
if "num_points_total" in d:
|
||||||
self.num_images.set(d['num_points_total']).wait()
|
self.num_images.set(d["num_points_total"]).wait()
|
||||||
|
|
||||||
# Restart the DAQ if resolution changed
|
# Restart the DAQ if resolution changed
|
||||||
cfg = self.get_daq_config()
|
cfg = self.get_daq_config()
|
||||||
if cfg['image_pixel_height'] != self.cfg_pixel_height.get() or \
|
if (
|
||||||
cfg['image_pixel_width'] != self.cfg_pixel_width.get() or \
|
cfg["image_pixel_height"] != self.cfg_pixel_height.get()
|
||||||
cfg['bit_depth'] != self.cfg_bit_depth.get() or \
|
or cfg["image_pixel_width"] != self.cfg_pixel_width.get()
|
||||||
cfg['number_of_writers'] != self.cfg_nr_writers.get():
|
or cfg["bit_depth"] != self.cfg_bit_depth.get()
|
||||||
|
or cfg["number_of_writers"] != self.cfg_nr_writers.get()
|
||||||
|
):
|
||||||
|
|
||||||
# Stop if current status is not idle
|
# Stop if current status is not idle
|
||||||
if self.state() != "idle":
|
if self.state() != "idle":
|
||||||
logger.warning(f"[{self.name}] stdDAQ reconfiguration might corrupt files")
|
logger.warning(f"[{self.name}] stdDAQ reconfiguration might corrupt files")
|
||||||
|
|
||||||
# Update retrieved config
|
# Update retrieved config
|
||||||
cfg['image_pixel_height'] = int(self.cfg_pixel_height.get())
|
cfg["image_pixel_height"] = int(self.cfg_pixel_height.get())
|
||||||
cfg['image_pixel_width'] = int(self.cfg_pixel_width.get())
|
cfg["image_pixel_width"] = int(self.cfg_pixel_width.get())
|
||||||
cfg['bit_depth'] = int(self.cfg_bit_depth.get())
|
cfg["bit_depth"] = int(self.cfg_bit_depth.get())
|
||||||
cfg['number_of_writers'] = int(self.cfg_nr_writers.get())
|
cfg["number_of_writers"] = int(self.cfg_nr_writers.get())
|
||||||
self.set_daq_config(cfg)
|
self.set_daq_config(cfg)
|
||||||
sleep(1)
|
sleep(1)
|
||||||
self.get_daq_config(update=True)
|
self.get_daq_config(update=True)
|
||||||
|
|
||||||
def bluestage(self):
|
def bluestage(self):
|
||||||
""" Stages the stdDAQ
|
"""Stages the stdDAQ
|
||||||
|
|
||||||
Opens a new connection to the stdDAQ, sends the start command with
|
Opens a new connection to the stdDAQ, sends the start command with
|
||||||
the current configuration. It waits for the first reply and checks
|
the current configuration. It waits for the first reply and checks
|
||||||
it for obvious failures.
|
it for obvious failures.
|
||||||
"""
|
"""
|
||||||
# Can't stage into a running exposure
|
# Can't stage into a running exposure
|
||||||
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
|
# Must make sure that image size matches the data source
|
||||||
@@ -315,10 +337,13 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
daq_img_h = self.cfg_pixel_height.get()
|
daq_img_h = self.cfg_pixel_height.get()
|
||||||
|
|
||||||
if not (daq_img_w == cam_img_w and daq_img_h == cam_img_h):
|
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})")
|
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:
|
else:
|
||||||
logger.warning(f"[{self.name}] stdDAQ image resolution ({daq_img_w} , {daq_img_h}) matches camera with ({cam_img_w} , {cam_img_h})")
|
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()
|
num_images = self.num_images.get()
|
||||||
@@ -327,7 +352,12 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
|
|
||||||
# New connection
|
# New connection
|
||||||
self._wsclient = self.connect()
|
self._wsclient = self.connect()
|
||||||
message = {"command": "start", "path": file_path, "file_prefix": file_prefix, "n_image": num_images, }
|
message = {
|
||||||
|
"command": "start",
|
||||||
|
"path": file_path,
|
||||||
|
"file_prefix": file_prefix,
|
||||||
|
"n_image": num_images,
|
||||||
|
}
|
||||||
reply = self.message(message)
|
reply = self.message(message)
|
||||||
|
|
||||||
if reply is not None:
|
if reply is not None:
|
||||||
@@ -338,8 +368,10 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
# Give it more time to reconfigure
|
# Give it more time to reconfigure
|
||||||
if reply["status"] in ("rejected"):
|
if reply["status"] in ("rejected"):
|
||||||
# FIXME: running exposure is a nogo
|
# FIXME: running exposure is a nogo
|
||||||
if reply['reason'] == "driver is busy!":
|
if reply["reason"] == "driver is busy!":
|
||||||
raise RuntimeError(f"[{self.name}] Start stdDAQ command rejected: already running")
|
raise RuntimeError(
|
||||||
|
f"[{self.name}] Start stdDAQ command rejected: already running"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# Give it more time to consolidate
|
# Give it more time to consolidate
|
||||||
sleep(1)
|
sleep(1)
|
||||||
@@ -348,16 +380,18 @@ 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(f"[{self.name}] Failed to start the stdDAQ in 1 tries, reason: {reply['reason']}")
|
raise RuntimeError(
|
||||||
|
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
|
||||||
|
|
||||||
Opens a new connection to the stdDAQ, sends the stop command and
|
Opens a new connection to the stdDAQ, sends the stop command and
|
||||||
waits for the idle state.
|
waits for the idle state.
|
||||||
"""
|
"""
|
||||||
ii = 0
|
ii = 0
|
||||||
while ii<10:
|
while ii < 10:
|
||||||
# Stop the DAQ (will close connection) - reply is always "success"
|
# Stop the DAQ (will close connection) - reply is always "success"
|
||||||
self._wsclient = self.connect()
|
self._wsclient = self.connect()
|
||||||
self.message({"command": "stop_all"}, wait_reply=False)
|
self.message({"command": "stop_all"}, wait_reply=False)
|
||||||
@@ -371,7 +405,7 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
if reply is not None:
|
if reply is not None:
|
||||||
logger.info(f"[{self.name}] DAQ status reply: {reply}")
|
logger.info(f"[{self.name}] DAQ status reply: {reply}")
|
||||||
reply = json.loads(reply)
|
reply = json.loads(reply)
|
||||||
|
|
||||||
if reply["status"] in ("idle", "error"):
|
if reply["status"] in ("idle", "error"):
|
||||||
# Only 'idle' state accepted
|
# Only 'idle' state accepted
|
||||||
print(f"DAQ stopped on try {ii}")
|
print(f"DAQ stopped on try {ii}")
|
||||||
@@ -388,6 +422,7 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
# Bluesky flyer interface
|
# Bluesky flyer interface
|
||||||
def complete(self) -> SubscriptionStatus:
|
def complete(self) -> SubscriptionStatus:
|
||||||
"""Wait for current run. Must end in status 'file_saved'."""
|
"""Wait for current run. Must end in status 'file_saved'."""
|
||||||
|
|
||||||
def is_running(*args, value, timestamp, **kwargs):
|
def is_running(*args, value, timestamp, **kwargs):
|
||||||
result = value in ["idle", "file_saved", "error"]
|
result = value in ["idle", "file_saved", "error"]
|
||||||
return result
|
return result
|
||||||
@@ -396,40 +431,35 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
return status
|
return status
|
||||||
|
|
||||||
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)
|
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
raise ConnectionError(f"[{self.name}] Error {r.status_code}:\t{r.text}")
|
raise ConnectionError(f"[{self.name}] Error {r.status_code}:\t{r.text}")
|
||||||
cfg = r.json()
|
cfg = r.json()
|
||||||
|
|
||||||
if update:
|
if update:
|
||||||
self.cfg_detector_name.set(cfg['detector_name']).wait()
|
self.cfg_detector_name.set(cfg["detector_name"]).wait()
|
||||||
self.cfg_detector_type.set(cfg['detector_type']).wait()
|
self.cfg_detector_type.set(cfg["detector_type"]).wait()
|
||||||
self.cfg_bit_depth.set(cfg['bit_depth']).wait()
|
self.cfg_bit_depth.set(cfg["bit_depth"]).wait()
|
||||||
self.cfg_pixel_height.set(cfg['image_pixel_height']).wait()
|
self.cfg_pixel_height.set(cfg["image_pixel_height"]).wait()
|
||||||
self.cfg_pixel_width.set(cfg['image_pixel_width']).wait()
|
self.cfg_pixel_width.set(cfg["image_pixel_width"]).wait()
|
||||||
self.cfg_nr_writers.set(cfg['number_of_writers']).wait()
|
self.cfg_nr_writers.set(cfg["number_of_writers"]).wait()
|
||||||
return cfg
|
return cfg
|
||||||
|
|
||||||
def set_daq_config(self, config, settle_time=1):
|
def set_daq_config(self, config, settle_time=1):
|
||||||
"""Write a full configuration to the DAQ
|
"""Write a full configuration to the DAQ"""
|
||||||
"""
|
url = self.rest_url.get() + "/api/config/set"
|
||||||
url = self.rest_url.get() + '/api/config/set'
|
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
url,
|
url,
|
||||||
params={"user": "ioc"},
|
params={"user": "ioc"},
|
||||||
json=config,
|
json=config,
|
||||||
timeout=2,
|
timeout=2,
|
||||||
headers={"Content-Type": "application/json"}
|
headers={"Content-Type": "application/json"},
|
||||||
)
|
)
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
raise ConnectionError(f"[{self.name}] Error {r.status_code}:\t{r.text}")
|
raise ConnectionError(f"[{self.name}] Error {r.status_code}:\t{r.text}")
|
||||||
# Wait for service to restart (and connect to make sure)
|
# Wait for service to restart (and connect to make sure)
|
||||||
#sleep(settle_time)
|
# sleep(settle_time)
|
||||||
self.connect()
|
self.connect()
|
||||||
return r.json()
|
return r.json()
|
||||||
|
|
||||||
@@ -437,20 +467,24 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
"""Combine the stddaq written files in a given folder in an interleaved
|
"""Combine the stddaq written files in a given folder in an interleaved
|
||||||
h5 virtual dataset
|
h5 virtual dataset
|
||||||
"""
|
"""
|
||||||
url = self.rest_url.get() + '/api/h5/create_interleaved_vds'
|
url = self.rest_url.get() + "/api/h5/create_interleaved_vds"
|
||||||
file_path = self.file_path.get()
|
file_path = self.file_path.get()
|
||||||
file_prefix = self.file_prefix.get()
|
file_prefix = self.file_prefix.get()
|
||||||
|
|
||||||
r = requests.post(
|
r = requests.post(
|
||||||
url,
|
url,
|
||||||
params = {'user': 'ioc'},
|
params={"user": "ioc"},
|
||||||
json = {'base_path': file_path, 'file_prefix': file_prefix, 'output_file': file_prefix.rstrip('_') + '.h5'},
|
json={
|
||||||
timeout = 2,
|
"base_path": file_path,
|
||||||
headers = {'Content-type': 'application/json'}
|
"file_prefix": file_prefix,
|
||||||
|
"output_file": file_prefix.rstrip("_") + ".h5",
|
||||||
|
},
|
||||||
|
timeout=2,
|
||||||
|
headers={"Content-type": "application/json"},
|
||||||
)
|
)
|
||||||
|
|
||||||
def nuke(self, restarttime=5):
|
def nuke(self, restarttime=5):
|
||||||
""" Reconfigures the stdDAQ to restart the services. This causes
|
"""Reconfigures the stdDAQ to restart the services. This causes
|
||||||
systemd to kill the current DAQ service and restart it with the same
|
systemd to kill the current DAQ service and restart it with the same
|
||||||
configuration. Which might corrupt the currently written file...
|
configuration. Which might corrupt the currently written file...
|
||||||
"""
|
"""
|
||||||
@@ -459,18 +493,20 @@ class StdDaqClient(PSIDeviceBase):
|
|||||||
sleep(restarttime)
|
sleep(restarttime)
|
||||||
|
|
||||||
def state(self) -> str | None:
|
def state(self) -> str | None:
|
||||||
""" Querry the current system status"""
|
"""Querry the current system status"""
|
||||||
try:
|
try:
|
||||||
wsclient = self.connect()
|
wsclient = self.connect()
|
||||||
wsclient.send(json.dumps({'command': 'status'}))
|
wsclient.send(json.dumps({"command": "status"}))
|
||||||
r = wsclient.recv(timeout=1)
|
r = wsclient.recv(timeout=1)
|
||||||
r = json.loads(r)
|
r = json.loads(r)
|
||||||
return r['status']
|
return r["status"]
|
||||||
except ConnectionRefusedError:
|
except ConnectionRefusedError:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
# Automatically connect to microXAS testbench if directly invoked
|
# Automatically connect to microXAS testbench if directly invoked
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
daq = StdDaqClient(name="daq", ws_url="ws://sls-daq-001:8080", rest_url="http://sls-daq-001:5000")
|
daq = StdDaqClient(
|
||||||
|
name="daq", ws_url="ws://sls-daq-001:8080", rest_url="http://sls-daq-001:5000"
|
||||||
|
)
|
||||||
daq.wait_for_connection()
|
daq.wait_for_connection()
|
||||||
|
|||||||
Reference in New Issue
Block a user