wip rio
This commit is contained in:
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user