From 701557f1f98f64af84ce781d58e2ae5a13f222a2 Mon Sep 17 00:00:00 2001 From: appel_c Date: Mon, 26 Jan 2026 08:16:31 +0100 Subject: [PATCH] wip galil rio --- csaxs_bec/devices/omny/galil/galil_rio.py | 107 ++++++++++++++-------- 1 file changed, 71 insertions(+), 36 deletions(-) diff --git a/csaxs_bec/devices/omny/galil/galil_rio.py b/csaxs_bec/devices/omny/galil/galil_rio.py index ac1302d..cc97005 100644 --- a/csaxs_bec/devices/omny/galil/galil_rio.py +++ b/csaxs_bec/devices/omny/galil/galil_rio.py @@ -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__":