From c10f1401f14f79c829176fa9ed3a6e76f6440fce Mon Sep 17 00:00:00 2001 From: appel_c Date: Tue, 3 Mar 2026 13:54:09 +0100 Subject: [PATCH] feat(socket): add cache mechanism for SocketSignal --- ophyd_devices/utils/socket.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/ophyd_devices/utils/socket.py b/ophyd_devices/utils/socket.py index 6d98887..5d3c3de 100644 --- a/ophyd_devices/utils/socket.py +++ b/ophyd_devices/utils/socket.py @@ -3,6 +3,7 @@ import functools import socket import time import typing +import uuid import numpy as np from bec_lib import bec_logger @@ -82,8 +83,28 @@ def data_type(val): class SocketSignal(abc.ABC, Signal): + """ + Base class for signals that interact with a socket connection. Subclasses + must implement the '_socket_get'and 'socket_set' methods to define how to + read from and write to the socket respectively. The signal also implements + an in-built caching mechanism if 'get' is called in a callback + from a subscription to avoid multiple socket reads in a recursion. It is important + that children keep the logic in 'read' and 'get' and implement the socket read logic + in '_socket_get' to ensure the caching mechanism works correctly. + + Args: + name (str): The name of the signal. + readback_timeout (float): Time in seconds to wait between socket read attempts before + returning cached value. + """ + SUB_SETPOINT = "setpoint" + def __init__(self, *, name, **kwargs): + + super().__init__(name=name, **kwargs) + self._active_socket_callbacks: set[str] = set() + @abc.abstractmethod def _socket_get(self): ... @@ -91,9 +112,22 @@ class SocketSignal(abc.ABC, Signal): def _socket_set(self, val): ... def get(self): + old_value = self._readback self._readback = self._socket_get() + timestamp = time.time() + self._metadata["timestamp"] = timestamp + self._run_subs( + sub_type=self.SUB_VALUE, old_value=old_value, value=self._readback, timestamp=timestamp + ) return self._readback + def _run_subs(self, *args, sub_type, **kwargs): + if sub_type in self._active_socket_callbacks: + return + self._active_socket_callbacks.add(sub_type) + super()._run_subs(*args, sub_type=sub_type, **kwargs) + self._active_socket_callbacks.remove(sub_type) + def put( self, value,