wip
CI for debye_bec / test (push) Failing after 50s

This commit is contained in:
x01da
2026-08-03 10:54:54 +02:00
parent 5dcb51f36c
commit d2c6b56510
3 changed files with 148 additions and 12 deletions
+119
View File
@@ -0,0 +1,119 @@
"""
ACS controller device exposing plain read/write variables (no motion).
Uses the same BEC building blocks as before:
- ophyd_devices.utils.controller.Controller -> shared TCP/IP communicator
- ophyd_devices.utils.socket.SocketIO -> raw socket helper
- ophyd_devices.utils.socket.SocketSignal -> Signal base talking through it
Protocol:
read: "?GETVAR(tag)" -> reply is the value
write: "SETVAR(value,tag)"
"""
from __future__ import annotations
from typing import TYPE_CHECKING
import numpy as np
from ophyd import Component as Cpt
from ophyd import Kind
from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase
from ophyd_devices.utils.controller import Controller, threadlocked
from ophyd_devices.utils.socket import SocketIO, SocketSignal
if TYPE_CHECKING:
from bec_lib.devicemanager import DeviceManagerBase
from bec_lib.logger import bec_logger
# Initialise logger
logger = bec_logger.logger
# ---------------------------------------------------------------------------
# Shared communicator
# ---------------------------------------------------------------------------
class ACSController(Controller):
"""
Shared TCP/IP communicator for one ACS controller.
Instantiating this class twice with the same (socket_host, socket_port)
returns the same object (see `Controller.__new__`), so every variable
signal below -- across however many devices -- shares one connection.
"""
_axes_per_controller = 0 # not used for plain variables, no motion axes
@threadlocked
def get_var(self, tag: int, precision=3) -> float:
if self.sock is None:
self.on()
i = 0
reply = self.socket_put_and_receive(f"?{{%0.{precision:0.0f}f}}GETVAR({tag},{i})")
return float(reply)
@threadlocked
def set_var(self, tag: int, value, precision=3) -> None:
if self.sock is None:
self.on()
i = 0
self.socket_put_and_receive(f"SETVAR({np.round(value, precision)},{tag},{i})")
# ---------------------------------------------------------------------------
# Signal talking through the shared controller
# ---------------------------------------------------------------------------
class ACSVariableSignal(SocketSignal):
"""Read/write ACS controller variable, identified by its tag number."""
def __init__(self, *args, tag: int, **kwargs):
self.tag = tag
super().__init__(*args, **kwargs)
@property
def controller(self) -> ACSController:
return self.root.controller
def _socket_get(self):
logger.info(self.controller)
logger.info(self.controller.sock)
return self.controller.get_var(self.tag)
def _socket_set(self, val):
self.controller.set_var(self.tag, val)
# ---------------------------------------------------------------------------
# The device
# ---------------------------------------------------------------------------
class ACSVariables(PSIDeviceBase):
"""Three read/write variables on an ACS controller, sharing one connection."""
var1 = Cpt(ACSVariableSignal, tag=1, kind=Kind.normal)
var2 = Cpt(ACSVariableSignal, tag=2, kind=Kind.normal)
var3 = Cpt(ACSVariableSignal, tag=3, kind=Kind.normal)
def __init__(
self,
name: str,
host: str,
port: int = 701,
device_manager: "DeviceManagerBase" | None = None,
**kwargs,
):
# controller must exist before super().__init__() builds the Cpt signals
self.controller = ACSController(
socket_cls=SocketIO, socket_host=host, socket_port=port, device_manager=device_manager
)
super().__init__(name=name, device_manager=device_manager, **kwargs)
def on_connected(self):
# Idempotent: safe even if other devices already opened this controller.
self.controller.on()
+9 -6
View File
@@ -13,6 +13,7 @@ from typing import Literal
from bec_lib.devicemanager import ScanInfo
from bec_lib.logger import bec_logger
from bec_server.device_server.devices.devicemanager import DeviceManagerDS
from bec_server.scan_server.scans.scan_base import ScanInfo as ScanServerScanInfo
from ophyd import Component as Cpt
from ophyd import DeviceStatus, StatusBase
@@ -59,7 +60,7 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner):
USER_ACCESS = ["set_advanced_xas_settings", "set_xtal", "convert_angle_energy"]
def __init__(self, name: str, prefix: str = "", scan_info: ScanInfo | None = None, **kwargs): # type: ignore
def __init__(self, name: str, prefix: str = "", scan_info: ScanInfo | None = None, device_manager: DeviceManagerDS | None = None, **kwargs): # type: ignore
"""
Initialize the PSI Device Base class.
@@ -67,7 +68,9 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner):
name (str) : Name of the device
scan_info (ScanInfo): The scan info to use.
"""
super().__init__(name=name, scan_info=scan_info, prefix=prefix, **kwargs)
super().__init__(
name=name, scan_info=scan_info, prefix=prefix, device_manager=device_manager, **kwargs
)
self.scan_parameters: ScanServerScanInfo = None
self.timeout_for_pvwait = 7.5
self.valid_scan_names = [
@@ -346,7 +349,7 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner):
self.cancel_on_stop(status)
logger.info(f"Finished calling complete on {self.name} within {time.time()-time_started}s.")
return status
def _status_callback(self, status, **kwargs):
logger.info(f"Complete finished on mo1bragg with {status.done} and {status.success}")
@@ -380,7 +383,7 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner):
if scan_parameters.scan_name in self.valid_scan_names:
return True
return False
def _progress_update(self, value, old_value, **kwargs) -> None:
"""Callback method to update the scan progress, runs a callback
to SUB_PROGRESS subscribers, i.e. BEC.
@@ -449,13 +452,13 @@ class Mo1Bragg(PSIDeviceBase, Mo1BraggPositioner):
in_signal = self.calculator.calc_energy
out_signal = self.calculator.calc_angle
else:
raise Mo1BraggError(f'Unknown mode {mode}')
raise Mo1BraggError(f"Unknown mode {mode}")
in_signal.put(inp)
status = CompareStatus(self.calculator.calc_done, 1)
self.cancel_on_stop(status)
status.wait(self.timeout_for_pvwait)
status = CompareStatus(out_signal, 0, operation_success='>')
status = CompareStatus(out_signal, 0, operation_success=">")
self.cancel_on_stop(status)
status.wait(self.timeout_for_pvwait)
return out_signal.get()
@@ -1,11 +1,14 @@
"""Module for the Mo1 Bragg positioner"""
from __future__ import annotations
import threading
import time
import traceback
from typing import Literal
from typing import TYPE_CHECKING, Literal
from bec_lib.logger import bec_logger
from bec_server.device_server.devices.devicemanager import DeviceManagerDS
from ophyd import Component as Cpt
from ophyd import (
Device,
@@ -17,8 +20,10 @@ from ophyd import (
Signal,
)
from ophyd.utils import LimitError
from ophyd_devices.utils.socket import SocketIO
from debye_bec.devices.mo1_bragg.acs_controller import ACSSignal
# from debye_bec.devices.mo1_bragg.acs_controller import ACSSignal
from debye_bec.devices.mo1_bragg.acs import ACSController, ACSVariableSignal
from debye_bec.devices.mo1_bragg.mo1_bragg_enums import MoveType
# Initialise logger
@@ -257,7 +262,9 @@ class Mo1BraggPositioner(Device, PositionerBase):
angle = Cpt(EpicsSignalRO, suffix="feedback_pos_angle_RBV", kind="normal", auto_monitor=True)
test = Cpt(ACSSignal, tag=53000) # s_scan_angle_low
# test = Cpt(ACSSignal, tag=53000) # s_scan_angle_low
test = Cpt(ACSVariableSignal, tag=53000, kind="normal") # s_scan_angle_low
########## Move Command PVs ##########
@@ -268,7 +275,7 @@ class Mo1BraggPositioner(Device, PositionerBase):
_default_sub = SUB_READBACK
SUB_PROGRESS = "progress"
def __init__(self, prefix="", *, name: str, **kwargs):
def __init__(self, prefix="", *, name: str, device_manager: DeviceManagerDS, **kwargs):
"""Initialize the Mo1 Bragg positioner.
Args:
@@ -276,13 +283,20 @@ class Mo1BraggPositioner(Device, PositionerBase):
name (str): Name of the device
kwargs: Additional keyword arguments
"""
host = "129.129.123.32"
port = 701
self.controller = ACSController(
socket_cls=SocketIO, socket_host=host, socket_port=port, device_manager=device_manager
)
super().__init__(prefix, name=name, **kwargs)
self._move_thread = None
self._stopped = False
self.readback.name = self.name
self.controller = ACSController(host, port)
kwargs["controller"] = self.controller
# self.controller = ACSController(host, port)
# kwargs["controller"] = self.controller
def stop(self, *, success=False) -> None:
"""Stop any motion on the positioner