wip beamline

This commit is contained in:
gac-x10da
2025-08-08 10:08:42 +02:00
committed by appel_c
parent 2376f12668
commit 19565e9bae
2 changed files with 43 additions and 36 deletions

View File

@@ -46,11 +46,12 @@ if __name__ == """__main__""":
# # mock_server = TimePixFlyMockServer()
# server = TimepixServer(name='server', prefix='X10DA-ES-TPX1:cam1:')
# Create a Timepix object
rest_url = "p4-0017.psi.ch:8452"
ws_url = "p4-0017:8452/ws"
data_server_host = "p4-0017.psi.ch"
rest_url = "p4-0017.psi.ch:8453"
ws_url = "p4-0017:8453/ws"
# data_server_host = "x10da-bec-001.psi.ch"
# port = 3030
timepix = Timepix(
name="TimePixDetector", rest_url=rest_url, ws_url=ws_url, data_server_host=data_server_host
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)

View File

@@ -10,6 +10,7 @@ import socket
import threading
import time
from typing import Literal
import atexit
from bec_lib.logger import bec_logger
from ophyd import ADBase
@@ -114,12 +115,15 @@ class Timepix(PSIDeviceBase, TimePixControl):
DATA_SERVER_PORT = 3015 # Default data server port for TimePix detector
rest_url = 'p4-0017.psi.ch:8452'
ws_url = 'p4-0017:8452/ws'
Prefix of EPICS Control is 'X10DA-ES-TPX1:'
"""
def __init__(
self,
*,
name,
prefix:str,
rest_url: str = "localhost:8452",
ws_url: str = "localhost:8452/ws",
scan_info=None,
@@ -142,14 +146,17 @@ 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, 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 or data_server_host == "localhost":
if data_server_host is None:
data_server_host = (
socket.gethostname()
) # TODO check if that works -> Wihtout 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 data_server_port is None:
data_server_port = 3015
self._data_server_host = data_server_host
@@ -161,7 +168,6 @@ class Timepix(PSIDeviceBase, TimePixControl):
self._data_server_started = threading.Event()
# Socket server
self._socket_server = None
self._socket_server_allowed_connections = 1 # How many ?
self._socket_server_timeout = 0.1
self._socket_server_buffer_size = 4096
# Data buffers
@@ -171,6 +177,8 @@ class Timepix(PSIDeviceBase, TimePixControl):
self._decoder = json.JSONDecoder()
# Wait timeout
self._pv_timeout = 5.0 # Default timeout for PV operations
# Register cleanup
atexit.register(self.on_destroy)
### Beamline specifi methods for the TimePix Detector integration ###
@@ -235,6 +243,7 @@ class Timepix(PSIDeviceBase, TimePixControl):
TRoiStep=1,
TRoiN=5000,
)
logger.info(config)
# Parse pixel map from scan info if needed, otherwise use some default pixel map.
pixel_map = PixelMap(
chips=[
@@ -337,7 +346,7 @@ class Timepix(PSIDeviceBase, TimePixControl):
def _stop_data_receiver(self):
if self._data_server_thread is not None and self._data_server_thread.is_alive():
self._data_server_thread_event.set()
self._data_server_thread.join(timeout=5.0)
self._data_server_thread.join(timeout=1.0)
if self._data_server_thread is not None and self._data_server_thread.is_alive():
logger.warning(
f"Data server thread did not stop gracefully for device {self.name}."
@@ -354,6 +363,7 @@ class Timepix(PSIDeviceBase, TimePixControl):
while not self._data_server_thread_event.is_set():
try:
self._data_server_started.set()
# logger.info(self._socket_server)
conn, addr = self._socket_server.accept()
logger.info(f"Accepted connection from {addr}")
with conn:
@@ -408,18 +418,13 @@ class Timepix(PSIDeviceBase, TimePixControl):
with self._rlock:
if self._socket_server:
try:
self._data_server_thread_event.set() # Signal the data server thread to stop
self._stop_data_receiver()
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
logger.warning(f"Failed to shutdown socket server gracefully. Error: {e}")
self._stop_data_receiver()
self._socket_server = None
self._data_server_thread.join(timeout=5.0)
if self._data_server_thread.is_alive():
logger.warning(
f"Data server thread for {self.name} did not stop gracefully. Forcing shutdown."
)
self._data_server_thread = None
logger.warning(f"Failed to shutdown socket server. Error: {e}")
def restart_data_receiver(self):
"""Restart the data receiver thread."""
@@ -440,24 +445,25 @@ class Timepix(PSIDeviceBase, TimePixControl):
# AF_INET6 for IPv6, use AF_INET for IPv4; for localhost this may be different depending on the system
# TODO add an os check if self._data_server_host is localhost
# self._socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #BEAMLINE USAGE!
addr_info = socket.getaddrinfo(
self._data_server_host, self._data_server_port, socket.AF_UNSPEC, socket.SOCK_STREAM
)
# Use the first valid address returned by getaddrinfo
af, socktype, proto, _, _ = addr_info[0]
self._socket_server = socket.socket(af, socktype, proto)
# addr_info = socket.getaddrinfo(
# self._data_server_host, self._data_server_port, socket.AF_UNSPEC, socket.SOCK_STREAM
# )
# # 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)
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._socket_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if af == socket.AF_INET6:
try:
self._socket_server.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
except (AttributeError, OSError):
# This option might not exist on some systems, so we handle the exception
pass
self._socket_server.bind((self._data_server_host, self._data_server_port))
self._socket_server.listen(self._socket_server_allowed_connections)
self._socket_server.bind(host_port_info)
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 {self._data_server_host}:{self._data_server_port}")
logger.info(f"Data server started on {host_port_info}")
# pylint: disable=protected-access
@@ -469,7 +475,7 @@ if __name__ == "__main__":
mock_server = TimePixFlyMockServer()
# Create a Timepix object
timepix = Timepix(name="TimePixDetector")
timepix = Timepix(name="TimePixDetector", prefix='')
timepix.on_connected()
timepix.stage()
timepix.pre_scan()