wip debugging at the beamline

This commit is contained in:
2025-09-01 08:09:12 +02:00
committed by appel_c
parent c9df08fa68
commit 959f8f8cbb
3 changed files with 66 additions and 47 deletions

View File

@@ -30,7 +30,7 @@ from superxas_bec.devices.timepix.timepix_fly_client.timepix_fly_interface impor
)
class AndStatus(_AndStatus):
class AndStatus(StatusBase):
"""Custom AndStatus for TimePix detector."""
def __init__(self, left: StatusBase | DeviceStatus, right: StatusBase | DeviceStatus, **kwargs):
@@ -54,8 +54,12 @@ class AndStatus(_AndStatus):
# At least one is done.
# If it failed, do not wait for the second one.
if (not l_success) and l_done:
if self._externally_initiated_completion is True:
return
self.set_exception(self.left.exception())
elif (not r_success) and r_done:
if self._externally_initiated_completion is True:
return
self.set_exception(self.right.exception())
elif l_success and r_success and l_done and r_done:
@@ -67,6 +71,25 @@ class AndStatus(_AndStatus):
self.left.add_callback(inner)
self.right.add_callback(inner)
def __repr__(self):
return "({self.left!r} & {self.right!r})".format(self=self)
def __str__(self):
return (
"{0}(done={1.done}, "
"success={1.success})"
"".format(self.__class__.__name__, self)
)
def __contains__(self, status: StatusBase) -> bool:
for child in [self.left, self.right]:
if child == status:
return True
if isinstance(child, AndStatus):
if status in child:
return True
return False
logger = bec_logger.logger
@@ -214,7 +237,7 @@ class Timepix(PSIDeviceBase, TimePixControl):
self._troistep = 1
self._troin = 5000
self._pv_timeout = 3
self._readout_time = 2e-3 # 2ms readout time
self._readout_time = 2.1e-3 # 2.1ms readout time to ensure readout is >2ms, required from ASI serval server..
self.r_lock = threading.RLock() # Lock to access the message buffer safely
super().__init__(
name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs
@@ -404,7 +427,7 @@ class Timepix(PSIDeviceBase, TimePixControl):
other_config = OtherConfigModel(
TRoiStep=self.troistep,
TRoiN=self.troin,
output_uri=f"tcp://{self.backend.hostname}:{self.backend.socket_port}",
output_uri=f"tcp:{self.backend.hostname}:{self.backend.socket_port}",
)
logger.info(f"Current TimePixFly configuration: {other_config}")
pixel_map = self.pixel_map
@@ -412,6 +435,7 @@ class Timepix(PSIDeviceBase, TimePixControl):
# Fetch the backend socket info
net_add = self.backend.timepix_fly_client.get_net_addresses()
logger.info(f"Using net_add for timepix_fly backend {net_add}")
self.cam.raw_file_template.set("").wait(timeout=self._pv_timeout)
self.cam.raw_file_path.set(f"tcp://connect@{net_add.address}").wait(
timeout=self._pv_timeout
@@ -433,36 +457,30 @@ class Timepix(PSIDeviceBase, TimePixControl):
"""Called when the device is triggered."""
def trigger_callback(status: DeviceStatus):
"""Trigger callback to start the acquisition."""
"""Trigger callback to start the acquisition."""
if status.done:
logger.info(f"Calling acquire on detector.")
status.device.cam.acquire.put(1)
logger.info(f"Status callback from backend trigger. done {status.done}, success {status.success} and exception {status._exception}")
# Detector will be ready to start, as either pre_scan or the status_camera from a previous
# trigger will ensure that the detector is in ACQUIRESTATUS.DONE state.
status_backend = DeviceStatus(self)
# Add callback that starts the acquisition on the detector
status_backend.add_callback(trigger_callback)
# Prepare the camera status that resolves when the camera is finished acquiring
status_camera = TransitionStatus(
self.cam.acquire_busy, [ACQUIRESTATUS.ACQUIRING, ACQUIRESTATUS.DONE]
self.cam.acquire_busy, [ACQUIRESTATUS.DONE, ACQUIRESTATUS.ACQUIRING, ACQUIRESTATUS.DONE]
)
# Prepare the backend, attach the status to the state of the backend
status_backend = self.backend.on_trigger(status=status_backend)
# TODO cleanup and test
def failed_to_start_collect_cb(status: DeviceStatus):
"""Callback to handle failure to start the collect."""
if not status.done:
logger.error("Failed to start collect on Timepix Fly backend.")
status.device.backend.timepix_fly_client.stop_collect()
status_collect_backend = DeviceStatus(self, timeout=10)
status_collect_backend.add_callback(failed_to_start_collect_cb)
self.backend.timepix_fly_client.add_status_callback(
status=status_collect_backend,
success=[TimePixFlyStatus.COLLECT],
error=[TimePixFlyStatus.EXCEPT, TimePixFlyStatus.SHUTDOWN],
error=[TimePixFlyStatus.EXCEPT, TimePixFlyStatus.SHUTDOWN, TimePixFlyStatus.CONFIG],
)
# Add callback that starts the acquisition on the detector
status_backend.add_callback(trigger_callback)
status = AndStatus(status_backend, status_camera)
st = AndStatus(status, status_collect_backend)
@@ -513,11 +531,11 @@ if __name__ == "__main__": # pragma: no cover
timepix.wait_for_connection(all_signals=True, timeout=10)
timepix.on_connected()
print("Timepix connected and initialized.")
for exp_time, frames_per_trigger in zip([10, 1], [1, 5]):
for exp_time, frames_per_trigger, runs in zip([0.1,1], [20,5], [10,5]):
time.sleep(0.5)
print(
f"Sleeping for 0.5 seconds before starting the scan with exp_time={exp_time} "
f"and frames_per_trigger={frames_per_trigger}."
f"and frames_per_trigger={frames_per_trigger}. and runs {runs}"
)
timepix.scan_info.msg.scan_parameters.update(
@@ -534,36 +552,24 @@ if __name__ == "__main__": # pragma: no cover
msgs = []
# for ii in range(runs):
print(f"Starting scan...; exp_time={exp_time}, frames_per_trigger={frames_per_trigger}")
status = timepix.trigger()
print("Timepix trigger sent.")
start_time = time.time()
while not status.done:
try:
status.wait(timeout=1)
except Exception as exc:
print(f" Trigger status not done yet after ({time.time() - start_time:.2f}s)")
if time.time() - start_time > 30:
print("Breaking loop manually after 30 seconds of waiting.")
break
# if timepix.xes_data.get() is not None:
# # msgs.append(timepix.backend.msg_buffer)
# print(
# f"Events in energy rois {timepix.xes_data.get().signals[timepix.xes_data.name]['value'].sum()}"
# )
# events = timepix.xes_info.get().signals[
# f"{timepix.xes_info.name}_tds_total_events"
# ]["value"]
# print(f"Total number of events: {events}")
for run in range(runs):
print(f"Starting run {run} for exp_time {exp_time}.")
status = timepix.trigger()
start_time = time.time()
while not status.done:
try:
status.wait(timeout=1)
except Exception as exc:
print(f" Trigger status not done yet after ({time.time() - start_time:.2f}s)")
if time.time() - start_time > 20:
print("Breaking loop manually after 20 seconds of waiting.")
break
n_messages = len(timepix._msg_dump)
logger.warning(f"Messages in Buffer is {len(timepix._msg_dump)}")#, with types: {[';'.join([var.get('type') for var in timepix._msg_dump])]}")
status = timepix.complete()
print("Waiting for timepix to complete.")
status.wait(timeout=10)
print("Timepix scan completed.")
n_messages = len(timepix._msg_dump)
logger.warning(
f"Received new messages: Length of Buffer is {n_messages}, last message {timepix._msg_dump.get(n_messages-1, 'N/A') if n_messages>0 else 'N/A'}"
)
timepix.unstage()
print("Timepix unstaged.")
except Exception as e:

View File

@@ -9,6 +9,7 @@ hooks for all the relevant ophyd interface, 'on_stage',
from __future__ import annotations
import time
import atexit
import json
import signal
@@ -122,9 +123,9 @@ class TimepixFlyBackend:
success=[TimePixFlyStatus.CONFIG],
error=[TimePixFlyStatus.EXCEPT, TimePixFlyStatus.SHUTDOWN],
)
if other_config.output_uri != f"tcp:{self.hostname}:{self.socket_port}":
other_config.output_uri = f"tcp:{self.hostname}:{self.socket_port}"
logger.info(f"Setting output URI to {other_config.output_uri}.")
# if other_config.output_uri != f"tcp:{self.hostname}:{self.socket_port}":
# other_config.output_uri = f"tcp:{self.hostname}:{self.socket_port}"
# logger.info(f"Setting output URI to {other_config.output_uri}.")
# Make sure backend is in config state
try:
status.wait(timeout=5.0)
@@ -137,6 +138,14 @@ class TimepixFlyBackend:
raise TimeoutError(
"Timepix Fly backend state did not reach config state. Most likely a timeout error. Please check log for detailed error message."
)
status = StatusBase()
self.timepix_fly_client.add_status_callback(
status,
success=[TimePixFlyStatus.CONFIG],
error=[TimePixFlyStatus.EXCEPT, TimePixFlyStatus.SHUTDOWN],
)
logger.info(f"Setting other config, backend {other_config}")
self.timepix_fly_client.set_other_config(other_config)
self.timepix_fly_client.set_pixel_map(pixel_map)
@@ -156,6 +165,8 @@ class TimepixFlyBackend:
Returns:
StatusBase | DeviceStatus: The status object that will be updated with the operation's result
"""
# TODO add check that backend is in CONFIG!
time.sleep(0.05)
if status is None:
status = StatusBase()
self.timepix_fly_client.add_status_callback(

View File

@@ -344,6 +344,7 @@ class TimepixFlyClient:
Start the TimePix detector by sending a GET request to the start endpoint.
This method is a wrapper around the REST API call to start the detector.
"""
logger.info(f"Start send from pixfly client")
self._get(get_cmd="?start=true")
self._started = True
@@ -478,6 +479,7 @@ class TimepixFlyClient:
raise ValueError(
f"Value must be an instance of OtherConfigModel. Received {type(other_config)}, {other_config}."
)
# logger.info(f"Value send via rest from set_other_config {other_config.model_dump()}")
self._put(put_cmd="other-config", value=other_config.model_dump(), put_response_model=None)
def get_net_addresses(self) -> NetAddresses: