refactor(ddg2): add check for negative pulse widths.
This commit is contained in:
@@ -42,6 +42,7 @@ from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase
|
|||||||
from csaxs_bec.devices.epics.delay_generator_csaxs.delay_generator_csaxs import (
|
from csaxs_bec.devices.epics.delay_generator_csaxs.delay_generator_csaxs import (
|
||||||
CHANNELREFERENCE,
|
CHANNELREFERENCE,
|
||||||
OUTPUTPOLARITY,
|
OUTPUTPOLARITY,
|
||||||
|
PROC_EVENT_MODE,
|
||||||
STATUSBITS,
|
STATUSBITS,
|
||||||
TRIGGERSOURCE,
|
TRIGGERSOURCE,
|
||||||
AllChannelNames,
|
AllChannelNames,
|
||||||
@@ -122,6 +123,8 @@ class DDG1(PSIDeviceBase, DelayGeneratorCSAXS):
|
|||||||
self.set_io_values(channel, **config)
|
self.set_io_values(channel, **config)
|
||||||
self.set_trigger(DEFAULT_TRIGGER_SOURCE)
|
self.set_trigger(DEFAULT_TRIGGER_SOURCE)
|
||||||
self.set_references_for_channels(DEFAULT_REFERENCES)
|
self.set_references_for_channels(DEFAULT_REFERENCES)
|
||||||
|
# Set proc status to passively update with 5Hz (0.2s)
|
||||||
|
self.state.proc_status_mode.put(PROC_EVENT_MODE.FREQ_5HZ)
|
||||||
|
|
||||||
def on_stage(self) -> DeviceStatus | StatusBase | None:
|
def on_stage(self) -> DeviceStatus | StatusBase | None:
|
||||||
"""
|
"""
|
||||||
@@ -153,6 +156,8 @@ class DDG1(PSIDeviceBase, DelayGeneratorCSAXS):
|
|||||||
If we don't then subsequent triggers may reach the DDG too early, and will be ignored. To
|
If we don't then subsequent triggers may reach the DDG too early, and will be ignored. To
|
||||||
avoid this, we've added the option to specify a delay via add_delay, default here is 50ms.
|
avoid this, we've added the option to specify a delay via add_delay, default here is 50ms.
|
||||||
"""
|
"""
|
||||||
|
status = CompareStatus(self.state.event_status, STATUSBITS.NONE)
|
||||||
|
self.cancel_on_stop(status)
|
||||||
mcs = self.device_manager.devices.get("mcs", None)
|
mcs = self.device_manager.devices.get("mcs", None)
|
||||||
if mcs is None:
|
if mcs is None:
|
||||||
logger.info(f"Did not find mcs card in current session")
|
logger.info(f"Did not find mcs card in current session")
|
||||||
@@ -172,22 +177,23 @@ class DDG1(PSIDeviceBase, DelayGeneratorCSAXS):
|
|||||||
status_acquiring.wait(
|
status_acquiring.wait(
|
||||||
timeout=10
|
timeout=10
|
||||||
) # 2 s wait for mcs card to start should be more than enough..
|
) # 2 s wait for mcs card to start should be more than enough..
|
||||||
|
status.wait(timeout=10)
|
||||||
st = DeviceStatus(self)
|
# Default timeout of 5 seconds + exposure time * frames_per_trigger
|
||||||
self.cancel_on_stop(st)
|
timeout = 5 + self.scan_info.msg.scan_parameters.get(
|
||||||
|
"exp_time", 0.1
|
||||||
|
) * self.scan_info.msg.scan_parameters.get("frames_per_trigger", 1)
|
||||||
|
status = CompareStatus(self.state.event_status, STATUSBITS.END_OF_DELAY, timeout=timeout)
|
||||||
|
self.cancel_on_stop(status)
|
||||||
self.trigger_shot.put(1, use_complete=True)
|
self.trigger_shot.put(1, use_complete=True)
|
||||||
time.sleep(self.scan_info.msg.scan_parameters["exp_time"])
|
|
||||||
self.cancel_on_stop(st)
|
|
||||||
status = self.wait_for_status(status=st, bit_event=STATUSBITS.END_OF_DELAY, timeout=10)
|
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def wait_for_status(
|
def wait_for_status(
|
||||||
self, status: StatusBase, bit_event: STATUSBITS, timeout: float = 2
|
self, status: DeviceStatus, bit_event: STATUSBITS, timeout: float = 2
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Wait for a event status bit to be set.
|
"""Wait for a event status bit to be set.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
status (StatusBase): The status object to update.
|
status (DeviceStatus): The status object to update.
|
||||||
bit_event (STATUSBITS): The event status bit to wait for.
|
bit_event (STATUSBITS): The event status bit to wait for.
|
||||||
timeout (float): Maximum time to wait for the event status bit to be set.
|
timeout (float): Maximum time to wait for the event status bit to be set.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -100,6 +100,10 @@ class DDG2(PSIDeviceBase, DelayGeneratorCSAXS):
|
|||||||
frames_per_trigger = self.scan_info.msg.scan_parameters["frames_per_trigger"]
|
frames_per_trigger = self.scan_info.msg.scan_parameters["frames_per_trigger"]
|
||||||
# a = t0
|
# a = t0
|
||||||
# a has reference to t0, b has reference to a
|
# a has reference to t0, b has reference to a
|
||||||
|
if any(exp_time < rt for rt in DEFAULT_READOUT_TIMES.values()):
|
||||||
|
raise ValueError(
|
||||||
|
f"Exposure time {exp_time} is too short for the readout times {DEFAULT_READOUT_TIMES}"
|
||||||
|
)
|
||||||
burst_pulse_width = exp_time - DEFAULT_READOUT_TIMES["ab"]
|
burst_pulse_width = exp_time - DEFAULT_READOUT_TIMES["ab"]
|
||||||
self.set_delay_pairs(channel="ab", delay=0, width=burst_pulse_width)
|
self.set_delay_pairs(channel="ab", delay=0, width=burst_pulse_width)
|
||||||
self.burst_enable(count=frames_per_trigger, delay=0, period=exp_time)
|
self.burst_enable(count=frames_per_trigger, delay=0, period=exp_time)
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ https://www.thinksrs.com/downloads/pdfs/manuals/DG645m.pdf
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import enum
|
import enum
|
||||||
from typing import Literal, TypedDict
|
|
||||||
import time
|
import time
|
||||||
|
from typing import Literal, TypedDict
|
||||||
|
|
||||||
from bec_lib.logger import bec_logger
|
from bec_lib.logger import bec_logger
|
||||||
from ophyd import Component as Cpt
|
from ophyd import Component as Cpt
|
||||||
@@ -76,9 +76,26 @@ class OUTPUTPOLARITY(enum.Enum):
|
|||||||
POSITIVE = 1
|
POSITIVE = 1
|
||||||
|
|
||||||
|
|
||||||
|
class PROC_EVENT_MODE(int, enum.Enum):
|
||||||
|
"""Read mode for MCS channels."""
|
||||||
|
|
||||||
|
PASSIVE = 0
|
||||||
|
EVENT = 1
|
||||||
|
IO_INTR = 2
|
||||||
|
FREQ_0_1HZ = 3
|
||||||
|
FREQ_0_2HZ = 4
|
||||||
|
FREQ_0_5HZ = 5
|
||||||
|
FREQ_1HZ = 6
|
||||||
|
FREQ_2HZ = 7
|
||||||
|
FREQ_5HZ = 8
|
||||||
|
FREQ_10HZ = 9
|
||||||
|
FREQ_100HZ = 10
|
||||||
|
|
||||||
|
|
||||||
class STATUSBITS(enum.IntFlag):
|
class STATUSBITS(enum.IntFlag):
|
||||||
"""Bit flags for the status signal of the delay generator."""
|
"""Bit flags for the status signal of the delay generator."""
|
||||||
|
|
||||||
|
NONE = 0 << 0 # No status bits set.
|
||||||
TRIG = 1 << 0 # Got a trigger.
|
TRIG = 1 << 0 # Got a trigger.
|
||||||
RATE = 1 << 1 # Got a trigger while a delay or burst was in progress.
|
RATE = 1 << 1 # Got a trigger while a delay or burst was in progress.
|
||||||
END_OF_DELAY = 1 << 2 # A delay cycle has completed.
|
END_OF_DELAY = 1 << 2 # A delay cycle has completed.
|
||||||
@@ -91,6 +108,7 @@ class STATUSBITS(enum.IntFlag):
|
|||||||
def describe(self) -> dict:
|
def describe(self) -> dict:
|
||||||
"""Return a description of the status bits."""
|
"""Return a description of the status bits."""
|
||||||
descriptions = {
|
descriptions = {
|
||||||
|
STATUSBITS.NONE: "No status bits set.",
|
||||||
STATUSBITS.TRIG: "Got a trigger.",
|
STATUSBITS.TRIG: "Got a trigger.",
|
||||||
STATUSBITS.RATE: "Got a trigger while a delay or burst was in progress.",
|
STATUSBITS.RATE: "Got a trigger while a delay or burst was in progress.",
|
||||||
STATUSBITS.END_OF_DELAY: "A delay cycle has completed.",
|
STATUSBITS.END_OF_DELAY: "A delay cycle has completed.",
|
||||||
@@ -114,7 +132,7 @@ class StatusBitsCompareStatus(SubscriptionStatus):
|
|||||||
*args,
|
*args,
|
||||||
event_type=None,
|
event_type=None,
|
||||||
timeout: float | None = None,
|
timeout: float | None = None,
|
||||||
add_delay:float|None = None,
|
add_delay: float | None = None,
|
||||||
settle_time: float = 0,
|
settle_time: float = 0,
|
||||||
run: bool = True,
|
run: bool = True,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
@@ -137,9 +155,9 @@ class StatusBitsCompareStatus(SubscriptionStatus):
|
|||||||
"""Callback for subscription status"""
|
"""Callback for subscription status"""
|
||||||
obj = kwargs.get("obj", None)
|
obj = kwargs.get("obj", None)
|
||||||
if obj is None:
|
if obj is None:
|
||||||
name = 'no object received'
|
name = "no object received"
|
||||||
else:
|
else:
|
||||||
name=obj.name
|
name = obj.name
|
||||||
if any((STATUSBITS(value) & state) == state for state in self._raise_states):
|
if any((STATUSBITS(value) & state) == state for state in self._raise_states):
|
||||||
self.set_exception(
|
self.set_exception(
|
||||||
ValueError(
|
ValueError(
|
||||||
@@ -147,7 +165,7 @@ class StatusBitsCompareStatus(SubscriptionStatus):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
if self._add_delay !=0:
|
if self._add_delay != 0:
|
||||||
time.sleep(self._add_delay)
|
time.sleep(self._add_delay)
|
||||||
|
|
||||||
return (STATUSBITS(value) & self._value) == self._value
|
return (STATUSBITS(value) & self._value) == self._value
|
||||||
@@ -378,17 +396,24 @@ class DelayGeneratorEventStatus(Device):
|
|||||||
"EventStatusLI",
|
"EventStatusLI",
|
||||||
name="event_status",
|
name="event_status",
|
||||||
kind=Kind.omitted,
|
kind=Kind.omitted,
|
||||||
auto_monitor=True,
|
|
||||||
doc="Event status register for the delay generator",
|
doc="Event status register for the delay generator",
|
||||||
)
|
)
|
||||||
proc_status = Cpt(
|
proc_status = Cpt(
|
||||||
EpicsSignal,
|
EpicsSignal,
|
||||||
"EventStatusLI.PROC",
|
"EventStatusLI.PROC",
|
||||||
name="proc_status",
|
name="proc_status",
|
||||||
|
auto_monitor=True,
|
||||||
kind=Kind.omitted,
|
kind=Kind.omitted,
|
||||||
doc="Poll and flush the latest event status register entry from the HW to the event_status signal",
|
doc="Poll and flush the latest event status register entry from the HW to the event_status signal",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
proc_status_mode = Cpt(
|
||||||
|
EpicsSignal,
|
||||||
|
"EventStatusLI.SCAN",
|
||||||
|
kind=Kind.omitted,
|
||||||
|
doc="Readout mode for transferring data from status buffer to the event_status signal.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DelayGeneratorCSAXS(Device):
|
class DelayGeneratorCSAXS(Device):
|
||||||
"""
|
"""
|
||||||
@@ -403,6 +428,13 @@ class DelayGeneratorCSAXS(Device):
|
|||||||
In addition, the io layer allows setting amplitude, offset and polarity for each pair.
|
In addition, the io layer allows setting amplitude, offset and polarity for each pair.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# USER_ACCESS = [
|
||||||
|
# "set_channel_reference",
|
||||||
|
# "set_references_for_channels",
|
||||||
|
# "set_io_values",
|
||||||
|
# "set_trigger",
|
||||||
|
# ]
|
||||||
|
|
||||||
_pv_timeout: float = 5 # Default timeout for PV operations in seconds
|
_pv_timeout: float = 5 # Default timeout for PV operations in seconds
|
||||||
|
|
||||||
# Front Panel
|
# Front Panel
|
||||||
@@ -686,11 +718,23 @@ class DelayGeneratorCSAXS(Device):
|
|||||||
}[channel]
|
}[channel]
|
||||||
|
|
||||||
def set_channel_reference(self, channel: LiteralChannels, reference_channel: CHANNELREFERENCE):
|
def set_channel_reference(self, channel: LiteralChannels, reference_channel: CHANNELREFERENCE):
|
||||||
|
"""Set the reference channel for a specific channel.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
channel (LiteralChannels): The channel to set the reference for.
|
||||||
|
reference_channel (CHANNELREFERENCE): The reference channel to set.
|
||||||
|
"""
|
||||||
self._get_literal_channel(channel).reference.put(reference_channel.value)
|
self._get_literal_channel(channel).reference.put(reference_channel.value)
|
||||||
|
|
||||||
def set_references_for_channels(
|
def set_references_for_channels(
|
||||||
self, channels_and_refs: list[tuple[LiteralChannels, CHANNELREFERENCE]]
|
self, channels_and_refs: list[tuple[LiteralChannels, CHANNELREFERENCE]]
|
||||||
):
|
):
|
||||||
|
"""Set the reference channels for multiple channels.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
channels_and_refs (list[tuple[LiteralChannels, CHANNELREFERENCE]]): A list of
|
||||||
|
tuples where each tuple contains a channel and its corresponding reference channel.
|
||||||
|
"""
|
||||||
for ch, ref in channels_and_refs:
|
for ch, ref in channels_and_refs:
|
||||||
self.set_channel_reference(ch, ref)
|
self.set_channel_reference(ch, ref)
|
||||||
|
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ class MCSCardCSAXS(PSIDeviceBase, MCSCard):
|
|||||||
self.counter_updated.append(signal.name)
|
self.counter_updated.append(signal.name)
|
||||||
received_all_updates = set(self.counter_updated) == set(self.counter_mapping.keys())
|
received_all_updates = set(self.counter_updated) == set(self.counter_mapping.keys())
|
||||||
if received_all_updates:
|
if received_all_updates:
|
||||||
self.ready_to_read.put(1) # Reset happens from DDG class!
|
self.ready_to_read.put(READYTOREAD.DONE) # Reset happens from DDG class!
|
||||||
self.counter_updated.clear()
|
self.counter_updated.clear()
|
||||||
|
|
||||||
def _progress_update(self, value, **kwargs) -> None:
|
def _progress_update(self, value, **kwargs) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user