fix(ddg): fix logic to resolve trigger on ddg1.
This commit is contained in:
@@ -488,12 +488,18 @@ class DDG1(PSIDeviceBase, DelayGeneratorCSAXS):
|
||||
def on_trigger(self) -> DeviceStatus:
|
||||
"""
|
||||
This method is called from BEC as a software trigger. Here the logic is as follows:
|
||||
We check the signal of the "fast_shutter_control". If it is low, we know that the shutter
|
||||
open/closes for each trigger cycle. In this case, we can use the signal of the
|
||||
"fast_shutter_readback" to check if the shutter transitioned from open to close, which
|
||||
indicates the end of the burst. If the "fast_shutter_control" signal is high, we know
|
||||
that the shutter is kept open during the scan. In this case, we can only rely on polling
|
||||
the event status register to check if the burst finished.
|
||||
|
||||
We first check if the trigger_source is set to SINGLE_SHOT. Only then will we received,
|
||||
otherwise we return a status object directly as the DDG is triggered by an external
|
||||
source which will have to implement its own logic to wait for trigger signals to
|
||||
be received.
|
||||
|
||||
I SINGLE_SHOT, the implementation here will send a software trigger. Now there are
|
||||
two options to wait for the trigger (burst) cycle to be done. One is to rely on the
|
||||
signal of the "mcs" card if it is present. However, this is only possible if the
|
||||
scan_type is not "fly" as in fly scans the ef channel is not triggered to send the last
|
||||
pulse to the card (but the card is finishing its acquisition in complete itself). Then
|
||||
we rely on the polling of the event status register to check if the burst cycle is done.
|
||||
|
||||
It follows a specific procedure to ensure that the DDG1 and MCS card are properly handled
|
||||
on a trigger event. The established logic is as follows:
|
||||
@@ -512,8 +518,8 @@ class DDG1(PSIDeviceBase, DelayGeneratorCSAXS):
|
||||
- Return the status object to BEC which will automatically resolve once the status register has
|
||||
the END_OF_BURST bit set. The callback of the status object will also stop the polling loop.
|
||||
"""
|
||||
overall_start = time.time()
|
||||
self._stop_polling()
|
||||
self._poll_thread_poll_loop_done.wait(timeout=1)
|
||||
|
||||
# NOTE If the trigger source is not SINGLE_SHOT, the DDG is triggered by an external source
|
||||
# thus we can not expect that trigger signals are meant to be awaited for. In this case,
|
||||
@@ -525,40 +531,33 @@ class DDG1(PSIDeviceBase, DelayGeneratorCSAXS):
|
||||
|
||||
# NOTE If the MCS card is present in the current session of BEC,
|
||||
# we prepare the card for the next trigger. The procedure is implemented
|
||||
# in the '_prepare_mcs_on_trigger' method.
|
||||
# Prepare the MCS card for the next software trigger
|
||||
# in the '_prepare_mcs_on_trigger' method. We will also use the mcs card
|
||||
# to indicate when the burst cycle is done. If no mcs card is available
|
||||
# the fallback is to use the polling of the DDG
|
||||
mcs = self.device_manager.devices.get("mcs", None)
|
||||
if mcs is None or mcs.enabled is False:
|
||||
logger.info("Did not find mcs card with name 'mcs' in current session")
|
||||
else:
|
||||
status_mcs = self._prepare_mcs_on_trigger(mcs)
|
||||
# NOTE Timeout of 3s should be plenty, any longer wait should checked. If this happens to crash
|
||||
# an acquisition regularly with a WaitTimeoutError, the timeout can be increased but it should
|
||||
# be investigated why the EPICS interface is slow to respond.
|
||||
status_mcs.wait(timeout=3)
|
||||
|
||||
# Use fast shutter readback in case it is not kept open
|
||||
# NOTE: THe update frequency of the EpicsSIgnal is ~10Hz (100ms), needs to be checked if that can
|
||||
# be adjusted to ~2kHz
|
||||
if (
|
||||
self.fast_shutter_control.get() != 1
|
||||
): # Shutter is not kept open, we can rely on its close signal.
|
||||
status = TransitionStatus(
|
||||
self.fast_shutter_readback, [1, 0]
|
||||
) # Add timeout error with explicit info update epics update freq
|
||||
else:
|
||||
# NOTE This sleep is needed for 20ms to make sure that the HW of the DDG is
|
||||
# again ready to process new commands. It was transferred from just after the
|
||||
# _stop_polling() call, as it should only be relevant in case of polling the
|
||||
# event status register, which may only be if the shutter is kept open.
|
||||
if mcs is None or mcs.enabled is False or self.scan_info.msg.scan_type == "fly":
|
||||
self._poll_thread_poll_loop_done.wait(timeout=1)
|
||||
logger.warning("Did not find mcs card with name 'mcs' in current session")
|
||||
time.sleep(0.02)
|
||||
# Shutter is kept open, we can only rely on the event status register
|
||||
status = self._prepare_trigger_status_event()
|
||||
# Start polling thread again to monitor event status
|
||||
self._start_polling()
|
||||
# Trigger the DDG1
|
||||
self.cancel_on_stop(status)
|
||||
else:
|
||||
start_time = time.time()
|
||||
logger.debug(f"Preparing mcs card ")
|
||||
status_mcs = self._prepare_mcs_on_trigger(mcs)
|
||||
# NOTE Timeout of 3s should be plenty, any longer wait should checked. If this happens to crash
|
||||
# an acquisition regularly with a WaitTimeoutError, the timeout can be increased but it should
|
||||
# be investigated why the EPICS interface is slow to respond.
|
||||
status_mcs.wait(timeout=3)
|
||||
status = TransitionStatus(mcs.acquiring, [ACQUIRING.ACQUIRING, ACQUIRING.DONE])
|
||||
logger.debug(f"Finished preparing mcs card {time.time()-start_time}")
|
||||
|
||||
# Send trigger
|
||||
self.trigger_shot.put(1, use_complete=True)
|
||||
self.cancel_on_stop(status)
|
||||
logger.info(f"Configured ddg in {time.time()-overall_start}")
|
||||
return status
|
||||
|
||||
def on_stop(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user