wip galil rio
CI for csaxs_bec / test (pull_request) Failing after 1m25s
CI for csaxs_bec / test (push) Failing after 1m38s

This commit is contained in:
2026-01-26 08:16:31 +01:00
parent 825f21fe7b
commit 701557f1f9
+71 -36
View File
@@ -6,10 +6,8 @@ Link to the Galil RIO vendor page:
https://www.galil.com/plcs/remote-io/rio-471xx
This module provides the GalilRIOController for communication with the RIO controller
over TCP/IP as well as a read-only Ophyd Signal, which reads from all 8 analog input channels.
This signal can be used to create virtual devices in BEC that process the analog input values
and combine them into more complex signals. For this purpose, the GalilRIOSignal should be
integrated as a 'monitored' or 'baseline' signal in the device config of BEC.
over TCP/IP. It also provides a device integration that interfaces to these
8 analog channels.
"""
from __future__ import annotations
@@ -70,33 +68,67 @@ class GalilRIOController(Controller):
class GalilRIOSignalRO(GalilSignalRO):
"""
Read-only Signal for reading a single analog input channel from the Galil RIO controller.
If the last reading of the controller is less than 0.5 seconds old, the cached value is returned.
"""
def __init__(self, signal_name: str, channel: int, **kwargs):
super().__init__(signal_name, **kwargs)
_NUM_ANALOG_CHANNELS = 8
def __init__(self, signal_name: str, channel: int, parent: GalilRIO, **kwargs):
super().__init__(signal_name, parent=parent, **kwargs)
self._channel = channel
self._metadata["connected"] = False
self._last_readback = self.parent._last_readback
def _socket_get(self) -> float:
"""Get command for the readback signal"""
cmd = f"MG@AN[{self._channel}]"
cmd = "MG@" + ",@".join([f"AN[{ii}]" for ii in range(self._NUM_ANALOG_CHANNELS)])
# old_val = self._readback
# cmd = f"MG@AN[{self._channel}]"
ret = self.controller.socket_put_and_receive(cmd)
timestamp = time.time()
self._metadata["timestamp"] = timestamp
return float(ret.strip().split(" ")[0])
# timestamp = time.time()
# self._metadata["timestamp"] = timestamp
values = [float(val) for val in ret.strip().split(" ")]
# This updates all channels' readbacks, including self._readback
self._update_all_channels(values)
return self._readback
# new_val = float(ret.strip().split(" ")[0])
# self._run_subs(
# sub_type=self.SUB_VALUE, old_value=old_val, value=new_val, timestamp=timestamp
# )
# return float(ret.strip().split(" ")[0])
def get(self):
"""Get current analog channel values from the Galil RIO controller."""
old_val = self._readback
if time.monotonic() - self.parent._last_readback < 0.5:
return self._readback
self._readback = self._socket_get()
self._run_subs(
sub_type=self.SUB_VALUE,
old_value=old_val,
value=self._readback,
timestamp=self._metadata.get("timestamp", time.time()),
)
self.parent._last_readback = time.monotonic()
return self._readback
def _update_all_channels(self, values: list[float]) -> None:
"""
Update all analog channels an_ch0 to an_ch7 from receiving the readback update on a signal.
Args:
values (list[float]): List of 8 float values corresponding to the analog channels.
They must be in order from an_ch0 to an_ch7.
"""
for walk in self.parent.walk_signals():
if walk.item.attr_name.startswith("an_ch"):
channel_index = int(walk.item.attr_name.split("_")[-1])
if 0 <= channel_index < len(values):
old_val = walk.item._readback
new_val = values[channel_index]
timestamp = time.time()
walk.item._metadata["timestamp"] = timestamp
walk.item._readback = new_val
walk.item._run_subs(
sub_type=walk.item.SUB_VALUE,
old_value=old_val,
value=new_val,
timestamp=timestamp,
)
class GalilRIO(PSIDeviceBase):
"""Base integration for the Galil RIO card in BEC."""
@@ -127,6 +159,7 @@ class GalilRIO(PSIDeviceBase):
self.controller = GalilRIOController(
socket_cls=socket_cls, socket_host=host, socket_port=port, device_manager=device_manager
)
self._last_readback: float = time.monotonic()
super().__init__(name=name, device_manager=device_manager, scan_info=scan_info, **kwargs)
self.controller.subscribe(
self._update_connection_state, event_type=self.SUB_CONNECTION_CHANGE
@@ -145,27 +178,29 @@ class GalilRIO(PSIDeviceBase):
for walk in self.walk_signals():
walk.item._metadata["connected"] = self.controller.connected
def read(self):
"""Read all 8 analog input channels from the Galil RIO controller."""
# Get number of channels
channels: list[tuple[int, str]] = []
res = {}
# def read(self):
# """Read all 8 analog input channels from the Galil RIO controller."""
# if time.monotonic() - self._last_readback < 0.5:
# This yields tuples of cpt, signal
for walk in self.walk_signals():
if isinstance(walk.item, GalilRIOSignalRO):
# pylint: disable=protected-access
channels.append((walk.item._channel, walk.item.name))
# # Get number of channels
# channels: list[tuple[int, str]] = []
# res = {}
# Read all channels in one command
cmd = "MG@" + ",@".join([f"AN[{ii}]" for ii, _ in channels])
logger.info(f"Reading Galil RIO channels with command: {cmd}")
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
# # This yields tuples of cpt, signal
# for walk in self.walk_signals():
# if isinstance(walk.item, GalilRIOSignalRO):
# # pylint: disable=protected-access
# channels.append((walk.item._channel, walk.item.name))
# # Read all channels in one command
# cmd = "MG@" + ",@".join([f"AN[{ii}]" for ii, _ in channels])
# logger.info(f"Reading Galil RIO channels with command: {cmd}")
# 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__":