Workaround on motor schema expected by BEC to move

This commit is contained in:
gac-x06da
2025-02-03 11:58:40 +01:00
committed by mohacsi_i
parent 6b4a175f78
commit a794e3f60d
3 changed files with 36 additions and 28 deletions

View File

@@ -11,7 +11,7 @@ Created on Wed Jan 29 2025
@author: mohacsi_i
"""
import numpy as np
from ophyd import Device, Component, EpicsSignal, Kind, Staged
from ophyd import Device, Component, EpicsSignal, EpicsSignalWithRBV, Kind, Staged
from ophyd.areadetector import NDDerivedSignal
@@ -20,24 +20,13 @@ from bec_lib import bec_logger
logger = bec_logger.logger
class SilentNDDerivedSignal(NDDerivedSignal):
def inverse(self, value):
"""Shape the flat array to send as a result of ``.get``"""
array_shape = self.derived_shape[: self.derived_ndims]
if not any(array_shape):
raise RuntimeWarning(f"Invalid array size {self.derived_shape}")
return self._readback
array_len = np.prod(array_shape)
if len(value) < array_len:
raise RuntimeWarning(
f"cannot reshape array of size {len(value)} "
f"into shape {tuple(array_shape)}. Check IOC configuration."
)
return self._readback
return np.asarray(value[:array_len]).reshape(array_shape)
"""Silent version of NDDerivedSignal, it does not spam the terminal on
every defective frame (shit happens, ok?)."""
def _array_shape_callback(self, **kwargs):
try:
super()._array_shape_callback(**kwargs)
except RuntimeError:
pass
class NDArrayPreview(Device):
@@ -56,13 +45,15 @@ class NDArrayPreview(Device):
_default_sub = SUB_MONITOR
# Status attributes
min_callback_time = Component(
EpicsSignalWithRBV, "MinCallbackTime", kind=Kind.config, put_complete=True)
array_size_x = Component(EpicsSignal, "ArraySize0_RBV", kind=Kind.config)
array_size_y = Component(EpicsSignal, "ArraySize1_RBV", kind=Kind.config)
array_size_z = Component(EpicsSignal, "ArraySize2_RBV", kind=Kind.config)
ndimensions = Component(EpicsSignal, "NDimensions_RBV", kind=Kind.config)
array_data = Component(EpicsSignal, "ArrayData", kind=Kind.omitted)
shaped_image = Component(
NDDerivedSignal,
SilentNDDerivedSignal,
derived_from="array_data",
shape=("array_size_z", "array_size_y", "array_size_x"),
num_dimensions="ndimensions",

View File

@@ -10,6 +10,7 @@ import time
import threading
from threading import Thread, Lock
import requests
from collections import OrderedDict
from requests.adapters import HTTPAdapter, Retry
from ophyd import Component, Kind, Signal, PVPositioner
from ophyd.status import SubscriptionStatus
@@ -57,10 +58,11 @@ class LimitedSmarGonSignal(Signal):
if value > hil:
raise ValueError(f"Target {value} outside of limits {self.limits}")
def put(self, value, *, timestamp=None, **kwargs):
def put(self, value, *, timestamp=None, force=False, metadata=None, **kwargs,):
"""Overriden put to add communication with smargopolo"""
# Validate new value and get timestamp
self.check_value(value)
if not force:
self.check_value(value)
if timestamp is None:
timestamp = time.time()
@@ -86,7 +88,7 @@ class SmarGonAxis(PVPositioner):
This class controls the SmarGon goniometer via the REST interface. All
SmarGon axes share a common mutex to manage actual HW access.
"""
USER_ACCESS = ["omove"]
USER_ACCESS = ["omove", "oldmove"]
# Status attributes
sg_url = Component(Signal, kind=Kind.config, metadata={"write_access": False})
@@ -133,7 +135,7 @@ class SmarGonAxis(PVPositioner):
)
def on_target():
"""NOTE: This assumes that both readback and setpoint is always up to date"""
"""Monitors the setpoint and readback and calculates the on_target flag"""
time.sleep(2)
while True:
# Read back target and setpoint values
@@ -154,11 +156,12 @@ class SmarGonAxis(PVPositioner):
self._mon = threading.Thread(target=on_target, daemon=True)
self._mon.start()
def move(self, position, wait=True, timeout=None, moved_cb=None):
def omove(self, position, wait=True, timeout=None, moved_cb=None):
"""Move command that's masked by BEC"""
return self.omove(position, wait, timeout, moved_cb)
self.done.put(0, force=True)
return self.move(position, wait, timeout, moved_cb)
def omove(self, position, wait=True, timeout=2.0, moved_cb=None):
def oldmove(self, position, wait=True, timeout=2.0, moved_cb=None):
"""Original move command without the BEC wrappers"""
status = self.setpoint.set(position, settle_time=0.1).wait()
if not wait:
@@ -172,7 +175,21 @@ class SmarGonAxis(PVPositioner):
status = SubscriptionStatus(self.readback, on_target, timeout=timeout, settle_time=0.1)
return status
def describe(self):
"""Workaround to schema expected by the BEC"""
d = super().describe()
d[str(self.name)] = d[f"{self.name}_readback"]
return d
def read(self) -> OrderedDict[str, dict]:
"""Workaround to schema expected by the BEC"""
d = super().read()
d[str(self.name)] = d[f"{self.name}_readback"]
return d
def _pos_changed(self, timestamp=None, value=None, **kwargs):
"""Remove EPICS dependency"""
pass
def _go_n_get(self, address, **kwargs):

View File

@@ -6,7 +6,7 @@ Ophyd devices for the PX III beamline, including the MX specific Aerotech A3200
"""
from .A3200 import AerotechAbrStage
from .A3200utils import A3200Axis
from .SmarGon import SmarGonAxis
from .SmarGon2 import SmarGonAxis
from .StdDaqPreview import StdDaqPreviewDetector
from .NDArrayPreview import NDArrayPreview
from .SamCamDetector import SamCamDetector