wip rio
CI for csaxs_bec / test (pull_request) Failing after 33s
CI for csaxs_bec / test (push) Failing after 36s

This commit is contained in:
2026-01-22 10:10:59 +01:00
parent 34be4c69e5
commit c7f82082f9
+90 -57
View File
@@ -17,21 +17,29 @@ from __future__ import annotations
import time
from typing import TYPE_CHECKING
from ophyd import Kind
from ophyd_devices import PSIDeviceBase
from ophyd_devices.utils.controller import Controller, threadlocked
from ophyd_devices.utils.socket import SocketIO, SocketSignal
from ophyd_devices.utils.socket import SocketIO
from csaxs_bec.devices.omny.galil.galil_ophyd import (
GalilCommunicationError,
ReadOnlyError,
GalilSignalRO,
retry_once,
)
if TYPE_CHECKING: # pragma: no cover
from bec_lib.devicemanager import ScanInfo
from bec_server.device_server.devices.devicemanager import DeviceManagerDS
class GalilRIOController(Controller):
"""Controller Class for Galil RIO controller communication."""
"""
Controller Class for Galil RIO controller communication.
Multiple controllers are in use at the cSAXS beamline:
- 129.129.98.64 (port 23)
"""
@threadlocked
def socket_put(self, val: str) -> None:
@@ -55,70 +63,26 @@ class GalilRIOController(Controller):
)
class GalilRIOSignal(SocketSignal):
class GalilRIOSignalRO(GalilSignalRO):
"""
Read-only Signal for reading _NUM_ANALOG_CH analog input channels from Galil RIO controller.
The signal reads all channels at once and returns a list of float values.
The host (129.129.98.64) and port (23) are set to the default values for the Galil RIO controller.
But can be configured during initialization.
Read-only Signal for reading a single analog input channel from the Galil RIO controller.
"""
_NUM_ANALOG_CH = 8
def __init__(self, signal_name: str, channel: int, **kwargs):
super().__init__(signal_name=signal_name, **kwargs)
self._channel = channel
def __init__(
self,
name: str,
host: str = "129.129.98.64",
port: int = 23,
socket_cls=SocketIO,
device_manager: DeviceManagerDS | None = None,
**kwargs,
):
super().__init__(name=name, **kwargs)
self._metadata["write_access"] = False
self.controller = GalilRIOController(
socket_cls=socket_cls, socket_host=host, socket_port=port, device_manager=device_manager
)
self._metadata["connected"] = False
self._readback = [0.0] * self._NUM_ANALOG_CH # Set proper initial value type
def wait_for_connection(self, timeout=10, **kwargs):
"""
Wait for socket connection to be established within timeout period.
This is needed to ensure that the controller is connected before starting
to read values.
Args:
timeout (int): Time in seconds to wait for connection
"""
self.controller.on(timeout=timeout)
self._metadata["connected"] = True
def destroy(self):
"""Make sure to turn off the controller socket on destroy."""
self.controller.off()
return super().destroy()
def _socket_set(self, val):
raise ReadOnlyError("Read-only signals cannot be set")
@threadlocked
def _socket_get(self) -> list[float]:
"""Get command for the readback signal
Returns:
list[float]: List of analog channel values
"""
cmd = "MG@" + ",@".join([f"AN[{ii}]" for ii in range(self._NUM_ANALOG_CH)])
def _socket_get(self) -> float:
"""Get command for the readback signal"""
cmd = f"MG@AN[{self._channel}]"
ret = self.controller.socket_put_and_receive(cmd)
timestamp = time.time()
self._metadata["timestamp"] = timestamp
return [float(val) for val in ret.strip().split(" ")]
return float(ret.strip().split(" ")[0])
def get(self):
"""Get current analog channel values from the Galil RIO controller."""
old_val = self._readback if isinstance(self._readback, list) else []
old_val = self._readback
self._readback = self._socket_get()
self._run_subs(
sub_type=self.SUB_VALUE,
@@ -127,3 +91,72 @@ class GalilRIOSignal(SocketSignal):
timestamp=self._metadata.get("timestamp", time.time()),
)
return self._readback
class GalilRIO(PSIDeviceBase):
"""Base integration for the Galil RIO card in BEC."""
an_ch1 = GalilRIOSignalRO("an_ch1", channel=1, doc="Analog input channel 1")
an_ch2 = GalilRIOSignalRO("an_ch2", channel=2, doc="Analog input channel 2")
an_ch3 = GalilRIOSignalRO("an_ch3", channel=3, doc="Analog input channel 3")
an_ch4 = GalilRIOSignalRO("an_ch4", channel=4, doc="Analog input channel 4")
an_ch5 = GalilRIOSignalRO("an_ch5", channel=5, doc="Analog input channel 5")
an_ch6 = GalilRIOSignalRO("an_ch6", channel=6, doc="Analog input channel 6")
an_ch7 = GalilRIOSignalRO("an_ch7", channel=7, doc="Analog input channel 7")
an_ch8 = GalilRIOSignalRO("an_ch8", channel=8, doc="Analog input channel 8")
def __init__(
self,
name: str,
host: str,
device_manager: DeviceManagerDS,
port: int | None = None,
socket_cls: type[SocketIO] = SocketIO,
scan_info: ScanInfo | None = None,
**kwargs,
):
if port is None:
port = 23 # Default port for Galil RIO controller
super().__init__(name=name, device_manager=device_manager, scan_info=scan_info, **kwargs)
self.controller = GalilRIOController(
socket_cls=socket_cls, host=host, port=port, device_manager=device_manager
)
self._metadata["connected"] = False
def wait_for_connection(self, timeout: float = 30.0, **kwargs) -> None:
"""Wait for the RIO controller to be connected within timeout period."""
self.controller.on(timeout=timeout)
self._metadata["connected"] = True
def destroy(self) -> None:
"""Make sure to turn off the controller socket on destroy."""
self.controller.off()
return super().destroy()
def read(self):
"""Read all 8 analog input channels from the Galil RIO controller."""
# Get number of channels
channels: list[tuple[int, str]] = [()]
res = super().read()
for _, signal in self._get_components_of_kind(Kind.normal): # This gets all signals
channels.append((signal._channel, signal.name)) # pylint: disable=protected-access
cmd = "MG@" + ",@".join([f"AN[{ii}]" for ii, _ in channels])
ret = self.controller.socket_put_and_receive(cmd)
timestamp = time.time()
values = [float(val) for val in ret.strip().split(" ")]
for val, (_, signal_name) in zip(values, channels):
res[signal_name] = {"value": val, "timestamp": timestamp}
return res
if __name__ == "__main__":
host = "129.129.98.64"
from bec_server.device_server.tests.utils import DMMock
dm = DMMock()
rio = GalilRIO("rio", host=host, device_manager=dm)
rio.wait_for_connection(timeout=10)
print("Connected:", rio.an_ch1.read())
print("All channels:", rio.read())