@@ -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()
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user