diff --git a/superxas_bec/devices/timepix/test_script.py b/superxas_bec/devices/timepix/test_script.py index eb63587..c11709f 100644 --- a/superxas_bec/devices/timepix/test_script.py +++ b/superxas_bec/devices/timepix/test_script.py @@ -51,7 +51,7 @@ if __name__ == """__main__""": # data_server_host = "x10da-bec-001.psi.ch" # port = 3030 timepix = Timepix( - name="TimePixDetector", prefix='X10DA-ES-TPX1:', rest_url=rest_url, ws_url=ws_url + name="TimePixDetector", prefix="X10DA-ES-TPX1:", rest_url=rest_url, ws_url=ws_url ) timepix.on_connected() timepix.wait_for_connection(all_signals=True, timeout=10) diff --git a/superxas_bec/devices/timepix/test_script_backup.py b/superxas_bec/devices/timepix/test_script_backup.py index 2b4619d..de931ef 100644 --- a/superxas_bec/devices/timepix/test_script_backup.py +++ b/superxas_bec/devices/timepix/test_script_backup.py @@ -1,5 +1,10 @@ if __name__ == """__main__""": + import threading import time + from unittest import mock + + import ophyd + from ophyd_devices.tests.utils import MockPV, patch_dual_pvs from superxas_bec.devices.timepix.timepix import Timepix from superxas_bec.devices.timepix.timepix_fly_client.timepix_fly_mock_server import ( @@ -7,24 +12,34 @@ if __name__ == """__main__""": ) mock_server = TimePixFlyMockServer() - timepix = Timepix(name="timepix", data_server_host="localhost") - timepix.on_connected() + with mock.patch.object(ophyd, "cl") as mock_cl: + mock_cl.get_pv = MockPV + mock_cl.thread_class = threading.Thread + timepix = Timepix(name="timepix", prefix="") + patch_dual_pvs(timepix) + timepix.timepix_fly_client.on_connected() + timepix._reset_buffers() + timepix.start_data_server() + # timepix.on_connected() - ## LOOP for a scan - timepix.stage() - status = timepix.pre_scan() - status.wait(timeout=10) - print(f"Pre-scan status: {status}") - mock_server.start_acquisition() - print("Acquisition started on mock server.") - # time.sleep(5) - status = timepix.complete() - status.wait(timeout=10) - for ii, msg in enumerate(timepix._data_buffer): - print(f"Message {ii}: {msg.keys()}") - print(f"Received data {ii} with message type {msg['type']}") - if msg["type"] == "EndFrame": - print(f"Received error message: {msg}") - # print(f"Data: {msg}") # Print first 50 characters of data - timepix.unstage() - timepix.destroy() + ## LOOP for a scan + timepix.stage() + status = timepix.pre_scan() + timepix.cam.acquire_busy._read_pv.mock_data = 1 + status.wait(timeout=10) + print(f"Pre-scan status: {status}") + mock_server.start_acquisition() + print("Acquisition started on mock server.") + # time.sleep(5) + status = timepix.complete() + time.sleep(8) + timepix.cam.acquire_busy._read_pv.mock_data = 0 + status.wait(timeout=10) + for ii, msg in enumerate(timepix._data_buffer): + print(f"Message {ii}: {msg.keys()}") + print(f"Received data {ii} with message type {msg['type']}") + if msg["type"] == "EndFrame": + print(f"Received error message: {msg}") + # print(f"Data: {msg}") # Print first 50 characters of data + timepix.unstage() + timepix.destroy() diff --git a/superxas_bec/devices/timepix/timepix.py b/superxas_bec/devices/timepix/timepix.py index 080dde2..0174aa2 100644 --- a/superxas_bec/devices/timepix/timepix.py +++ b/superxas_bec/devices/timepix/timepix.py @@ -3,6 +3,7 @@ TimePix Detector class for interfacing with the TimePix detector. The timepix_si implements the HTTP communication to the REST API for the tpx3app app. """ +import atexit import enum import json import signal @@ -10,7 +11,6 @@ import socket import threading import time from typing import Literal -import atexit from bec_lib.logger import bec_logger from ophyd import ADBase @@ -123,13 +123,13 @@ class Timepix(PSIDeviceBase, TimePixControl): self, *, name, - prefix:str, + prefix: str, rest_url: str = "localhost:8452", ws_url: str = "localhost:8452/ws", scan_info=None, device_manager=None, - data_server_host: str | None = None, - data_server_port: int | None = None, + hostname: str | None = None, + host_port: int | None = None, **kwargs, ): """ @@ -146,21 +146,22 @@ class Timepix(PSIDeviceBase, TimePixControl): data_server_port (int, optional): Port for the data server. Defaults to DATA_SERVER_PORT. **kwargs: Additional keyword arguments for the PSIDeviceBase class. """ - super().__init__(name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs) + super().__init__( + name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs + ) self._ws_url = ws_url self._rest_url = rest_url self.timepix_fly_client = TimepixFlyClient(rest_url=rest_url, ws_url=ws_url, parent=self) - if data_server_host is None: - data_server_host = ( - socket.getfqdn() - ) - if not data_server_host.endswith('.psi.ch'): - logger.info(f"Found host without psi.ch domain {data_server_host}") + if hostname is None: + hostname = socket.getfqdn() + if not hostname.endswith(".psi.ch"): + logger.info(f"Found host without psi.ch domain {hostname}") - if data_server_port is None: - data_server_port = 3015 - self._data_server_host = data_server_host - self._data_server_port = data_server_port + if host_port is None: + host_port = 3015 + self._hostname = hostname + self._data_server_host: str | None = None + self._data_server_port = host_port self._rlock = threading.RLock() # Data server self._data_server_thread = None @@ -237,6 +238,8 @@ class Timepix(PSIDeviceBase, TimePixControl): success=[TimePixFlyStatus.CONFIG], error=[TimePixFlyStatus.EXCEPT, TimePixFlyStatus.SHUTDOWN], ) + if self._data_server_host is None: + raise RuntimeError(f"Data server host is not set for device {self.name}.") # Parse scan info for OtherConfig config = OtherConfigModel( output_uri=f"tcp:{self._data_server_host}:{self._data_server_port}", @@ -419,8 +422,10 @@ class Timepix(PSIDeviceBase, TimePixControl): if self._socket_server: try: self._stop_data_receiver() - except Exception as e : - logger.info(f"Failed to stop data receiver for device {self.name} with exception {e}.") + except Exception as e: + logger.info( + f"Failed to stop data receiver for device {self.name} with exception {e}." + ) try: self._socket_server.close() except Exception as e: # pylint: disable=broad-except @@ -451,16 +456,23 @@ class Timepix(PSIDeviceBase, TimePixControl): # # Use the first valid address returned by getaddrinfo # af, socktype, proto, _, _ = addr_info[0] # self._socket_server = socket.socket(af, socktype, proto) - info = socket.getaddrinfo(self._data_server_host, port=self._data_server_port, family=socket.AF_INET, type=socket.SOCK_STREAM) + info = socket.getaddrinfo( + self._data_server_host, + port=self._data_server_port, + family=socket.AF_INET, + type=socket.SOCK_STREAM, + ) if len(info) == 0: raise RuntimeError(f"No socket info found") if len(info) > 1: logger.warning(f"Found multiple socket interfaces {info}, using the first one") af, socktype, proto, _, host_port_info = info[0] - self._socket_server = socket.socket(af, socktype, proto) + self._data_server_host = host_port_info[0] # Hostname or IP address + self._data_server_port = host_port_info[1] # Port number, should be the same as before + self._socket_server = socket.socket(af, socktype, proto) self._socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self._socket_server.bind(host_port_info) - self._socket_server.listen(1) # Only allow one connection + self._socket_server.listen(1) # Only allow one connection self._socket_server.settimeout(self._socket_server_timeout) self._start_data_receiver() logger.info(f"Data server started on {host_port_info}") @@ -475,7 +487,7 @@ if __name__ == "__main__": mock_server = TimePixFlyMockServer() # Create a Timepix object - timepix = Timepix(name="TimePixDetector", prefix='') + timepix = Timepix(name="TimePixDetector", prefix="") timepix.on_connected() timepix.stage() timepix.pre_scan()