diff --git a/csaxs_bec/devices/panda_box/panda_box.py b/csaxs_bec/devices/panda_box/panda_box.py index f7b8271..f881ec4 100644 --- a/csaxs_bec/devices/panda_box/panda_box.py +++ b/csaxs_bec/devices/panda_box/panda_box.py @@ -15,6 +15,7 @@ class PandaBoxCSAXS(PandaBox): def on_init(self): super().on_init() self._acquisition_group = "burst" + self._timeout_on_completed = 10 def on_stage(self): # TODO, adjust as seen fit. @@ -31,19 +32,19 @@ class PandaBoxCSAXS(PandaBox): """On complete is called after the scan is complete. We need to wait for the capture to complete before we can disarm the PandaBox.""" def _check_capture_complete(): - timeout = 10 captured = 0 - start_time = time.monotonic_ns() + start_time = time.monotonic() try: - while captured < self.scan_info.msg.num_points: - logger.debug( - f"Run with captured {captured} and expected points : {self.scan_info.msg.num_points}." + expected_points = int(self.scan_info.msg.num_points * self.scan_info.msg.scan_parameters.get("frames_per_trigger",1)) + while captured < expected_points: + logger.info( + f"Run with captured {captured} and expected points : {expected_points}." ) ret = self.send_raw("*PCAP.CAPTURED?") captured = int(ret[0].split("=")[-1]) - time.sleep(0.5) - if (time.monotonic_ns() - start_time) > timeout: - break + time.sleep(0.01) + if (time.monotonic() - start_time) > self._timeout_on_completed: + raise TimeoutError(f"Pandabox {self.name} did not complete after {self._timeout_on_completed} with points captured {captured}/{expected_points}") finally: self._disarm() @@ -61,14 +62,14 @@ class PandaBoxCSAXS(PandaBox): # ) # ret_status = status & status_panda_state # self.cancel_on_stop(ret_status) - return ret_status + # return ret_status if __name__ == "__main__": import time panda = PandaBoxCSAXS( - name="panda", + name="omny_panda", host="omny-panda.psi.ch", signal_alias={ "FMC_IN.VAL2.Value": "alias", diff --git a/csaxs_bec/devices/panda_box/panda_box_omny.py b/csaxs_bec/devices/panda_box/panda_box_omny.py new file mode 100644 index 0000000..7895289 --- /dev/null +++ b/csaxs_bec/devices/panda_box/panda_box_omny.py @@ -0,0 +1,102 @@ +"""Module to integrate the PandaBox for cSAXS measurements.""" + +import time + +from bec_lib.logger import bec_logger +from ophyd_devices import AsyncMultiSignal, StatusBase +from ophyd_devices.devices.panda_box.panda_box import PandaBox, PandaState +from pandablocks.responses import FrameData + +logger = bec_logger.logger + + +class PandaBoxOMNY(PandaBox): + + def on_init(self): + super().on_init() + self._acquisition_group = "burst" + self._timeout_on_completed = 10 + + def on_stage(self): + + # TODO, adjust as seen fit. + # Adjust the acquisition group based on scan parameters if needed + if self.scan_info.msg.scan_type == "fly": + self._acquisition_group = "fly" + elif self.scan_info.msg.scan_type == "step": + if self.scan_info.msg.scan_parameters["frames_per_trigger"] == 1: + self._acquisition_group = "monitored" + else: + self._acquisition_group = "burst" + + def on_complete(self): + """On complete is called after the scan is complete. We need to wait for the capture to complete before we can disarm the PandaBox.""" + + def _check_capture_complete(): + captured = 0 + start_time = time.monotonic() + try: + expected_points = int(self.scan_info.msg.num_points * self.scan_info.msg.scan_parameters.get("frames_per_trigger",1)) + while captured < expected_points: + logger.info( + f"Run with captured {captured} and expected points : {expected_points}." + ) + ret = self.send_raw("*PCAP.CAPTURED?") + captured = int(ret[0].split("=")[-1]) + time.sleep(0.01) + if (time.monotonic() - start_time) > self._timeout_on_completed: + raise TimeoutError(f"Pandabox {self.name} did not complete after {self._timeout_on_completed} with points captured {captured}/{expected_points}") + finally: + self._disarm() + + _check_capture_complete() + + # NOTE: This utility class allows to submit a blocking function to a thread and return a status object + # that can be awaited for. This allows for asynchronous waiting for the capture to complete without blocking + # the main duty cycle of the device server. The device server knows how to handle the status object (future) + # and will wait for it to complete. + # status = self.task_handler.submit_task(_check_capture_complete, run=True) + + # status_panda_state = StatusBase(obj=self) + # self.add_status_callback( + # status, success=[PandaState.END, PandaState.DISARMED], failure=[PandaState.READY] + # ) + # ret_status = status & status_panda_state + # self.cancel_on_stop(ret_status) + # return ret_status + + +if __name__ == "__main__": + import time + + panda = PandaBoxCSAXS( + name="omny_panda", + host="omny-panda.psi.ch", + signal_alias={ + "FMC_IN.VAL2.Value": "alias", + "FMC_IN.VAL1.Min": "alias2", + "FMC_IN.VAL1.Max": "alias3", + "FMC_IN.VAL1.Mean": "alias4", + }, + ) + panda.on_connected() + status = StatusBase(obj=panda) + panda.add_status_callback( + status=status, success=[PandaState.DISARMED], failure=[PandaState.READY] + ) + panda.stop() + status.wait(timeout=2) + panda.unstage() + logger.info(f"Panda connected") + ret = panda.stage() + logger.info(f"Panda staged") + ret = panda.pre_scan() + ret.wait(timeout=5) + logger.info(f"Panda pre scan done") + time.sleep(5) + panda.stop() + st = panda.complete() + st.wait(timeout=5) + logger.info(f"Measurement completed") + panda.unstage() + logger.info(f"Panda Unstaged")