mirror of
https://github.com/bec-project/ophyd_devices.git
synced 2025-06-24 19:51:09 +02:00
Merge branch 'xtreme_devices_update' into 'master'
Xtreme devices update See merge request bec/ophyd_devices!19
This commit is contained in:
@ -102,14 +102,46 @@ signals:
|
||||
onFailure: retry
|
||||
status: {enabled: true}
|
||||
|
||||
manipulator:
|
||||
description: 'Sample Manipulator'
|
||||
deviceClass: X07MASampleManipulator
|
||||
deviceConfig: {name: manipulator, prefix: 'X07MA-ES1-MAG:'}
|
||||
sample_hor:
|
||||
description: 'Horizontal sample position'
|
||||
deviceClass: EpicsMotor
|
||||
deviceConfig: {name: sample_hor, prefix: 'X07MA-ES1-AI:TRZS'}
|
||||
acquisitionConfig: {acquisitionGroup: monitor, readoutPriority: baseline, schedule: sync}
|
||||
onFailure: retry
|
||||
status: {enabled: true}
|
||||
|
||||
sample_vert:
|
||||
description: 'Horizontal sample position'
|
||||
deviceClass: EpicsMotor
|
||||
deviceConfig: {name: sample_vert, prefix: 'X07MA-ES1-AI:TRY1'}
|
||||
acquisitionConfig: {acquisitionGroup: monitor, readoutPriority: baseline, schedule: sync}
|
||||
onFailure: retry
|
||||
status: {enabled: true}
|
||||
|
||||
sample_rot:
|
||||
description: 'Horizontal sample position'
|
||||
deviceClass: EpicsMotor
|
||||
deviceConfig: {name: sample_rot, prefix: 'X07MA-ES1-AI:ROY1'}
|
||||
acquisitionConfig: {acquisitionGroup: monitor, readoutPriority: baseline, schedule: sync}
|
||||
onFailure: retry
|
||||
status: {enabled: true}
|
||||
|
||||
harmonic:
|
||||
description: 'ID harmonic'
|
||||
deviceClass: EpicsSignal
|
||||
deviceConfig: {name: harmonic, prefix: 'X07MA-ID:HARMONIC'}
|
||||
acquisitionConfig: {acquisitionGroup: monitor, readoutPriority: baseline, schedule: sync}
|
||||
onFailure: retry
|
||||
status: {enabled: true}
|
||||
|
||||
# manipulator:
|
||||
# description: 'Sample Manipulator'
|
||||
# deviceClass: X07MASampleManipulator
|
||||
# deviceConfig: {name: manipulator, prefix: 'X07MA-ES1-MAG:'}
|
||||
# acquisitionConfig: {acquisitionGroup: monitor, readoutPriority: baseline, schedule: sync}
|
||||
# onFailure: retry
|
||||
# status: {enabled: true}
|
||||
|
||||
temperature:
|
||||
description: 'Temperature controller'
|
||||
deviceClass: X07MATemperatureController
|
||||
@ -118,10 +150,10 @@ temperature:
|
||||
onFailure: retry
|
||||
status: {enabled: true}
|
||||
|
||||
TControl:
|
||||
tcontrol:
|
||||
description: 'Automatic temperature control'
|
||||
deviceClass: X07MAAutoTemperatureControl
|
||||
deviceConfig: {name: 'TControl', prefix: 'X07MA-ES1-TEMP:'}
|
||||
deviceConfig: {name: 'tcontrol', prefix: 'X07MA-ES1-TEMP:'}
|
||||
acquisitionConfig: {acquisitionGroup: monitor, readoutPriority: baseline, schedule: sync}
|
||||
onFailure: retry
|
||||
status: {enabled: true}
|
||||
|
@ -1,19 +1,19 @@
|
||||
"""
|
||||
ophyd device classes for X07MA beamline
|
||||
"""
|
||||
from collections import OrderedDict
|
||||
import time
|
||||
import traceback
|
||||
from collections import OrderedDict
|
||||
from typing import Any
|
||||
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd import FormattedComponent as FCpt
|
||||
|
||||
from ophyd import Device, EpicsSignal, EpicsSignalRO, Kind, PVPositioner, EpicsMotor
|
||||
from ophyd.flyers import FlyerInterface
|
||||
from ophyd.status import DeviceStatus, SubscriptionStatus
|
||||
from ophyd.pv_positioner import PVPositionerComparator
|
||||
import traceback
|
||||
from bec_utils import bec_logger
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO
|
||||
from ophyd import FormattedComponent as FCpt
|
||||
from ophyd import Kind, PVPositioner
|
||||
from ophyd.flyers import FlyerInterface
|
||||
from ophyd.pv_positioner import PVPositionerComparator
|
||||
from ophyd.status import DeviceStatus, SubscriptionStatus
|
||||
|
||||
logger = bec_logger.logger
|
||||
|
||||
|
@ -1 +1,2 @@
|
||||
from .sim import SynAxisMonitor, SynAxisOPAAS, SynFlyer, SynSignalRO, SynSLSDetector
|
||||
from .sim_xtreme import SynXtremeOtf
|
||||
|
@ -7,7 +7,7 @@ from typing import List
|
||||
import numpy as np
|
||||
from bec_utils import BECMessage, MessageEndpoints, bec_logger
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd import Device, DeviceStatus, PositionerBase, Signal
|
||||
from ophyd import Device, DeviceStatus, OphydObject, PositionerBase, Signal
|
||||
from ophyd.sim import _ReadbackSignal, _SetpointSignal
|
||||
from ophyd.utils import LimitError, ReadOnlyError
|
||||
|
||||
@ -438,7 +438,109 @@ class SynFlyer(Device, PositionerBase):
|
||||
flyer = threading.Thread(target=produce_data, args=(self, metadata))
|
||||
flyer.start()
|
||||
|
||||
# time.sleep(0.01)
|
||||
|
||||
class SynController(OphydObject):
|
||||
def on(self):
|
||||
pass
|
||||
|
||||
def off(self):
|
||||
pass
|
||||
|
||||
|
||||
class SynFlyerLamNI(Device, PositionerBase):
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
name,
|
||||
readback_func=None,
|
||||
value=0,
|
||||
delay=0,
|
||||
speed=1,
|
||||
update_frequency=2,
|
||||
precision=3,
|
||||
parent=None,
|
||||
labels=None,
|
||||
kind=None,
|
||||
device_manager=None,
|
||||
**kwargs,
|
||||
):
|
||||
if readback_func is None:
|
||||
|
||||
def readback_func(x):
|
||||
return x
|
||||
|
||||
sentinel = object()
|
||||
loop = kwargs.pop("loop", sentinel)
|
||||
if loop is not sentinel:
|
||||
warnings.warn(
|
||||
f"{self.__class__} no longer takes a loop as input. "
|
||||
"Your input will be ignored and may raise in the future",
|
||||
stacklevel=2,
|
||||
)
|
||||
self.sim_state = {}
|
||||
self._readback_func = readback_func
|
||||
self.delay = delay
|
||||
self.precision = precision
|
||||
self.tolerance = kwargs.pop("tolerance", 0.5)
|
||||
self.device_manager = device_manager
|
||||
|
||||
# initialize values
|
||||
self.sim_state["readback"] = readback_func(value)
|
||||
self.sim_state["readback_ts"] = ttime.time()
|
||||
|
||||
super().__init__(name=name, parent=parent, labels=labels, kind=kind, **kwargs)
|
||||
self.controller = SynController(name="SynController")
|
||||
|
||||
def kickoff(self, metadata, num_pos, positions, exp_time: float = 0):
|
||||
positions = np.asarray(positions)
|
||||
|
||||
def produce_data(device, metadata):
|
||||
buffer_time = 0.2
|
||||
elapsed_time = 0
|
||||
bundle = BECMessage.BundleMessage()
|
||||
for ii in range(num_pos):
|
||||
bundle.append(
|
||||
BECMessage.DeviceMessage(
|
||||
signals={
|
||||
"syn_flyer_lamni": {
|
||||
"flyer_samx": {"value": positions[ii, 0], "timestamp": 0},
|
||||
"flyer_samy": {"value": positions[ii, 1], "timestamp": 0},
|
||||
}
|
||||
},
|
||||
metadata={"pointID": ii, **metadata},
|
||||
).dumps()
|
||||
)
|
||||
ttime.sleep(exp_time)
|
||||
elapsed_time += exp_time
|
||||
if elapsed_time > buffer_time:
|
||||
elapsed_time = 0
|
||||
device.device_manager.producer.send(
|
||||
MessageEndpoints.device_read(device.name), bundle.dumps()
|
||||
)
|
||||
bundle = BECMessage.BundleMessage()
|
||||
device.device_manager.producer.set_and_publish(
|
||||
MessageEndpoints.device_status(device.name),
|
||||
BECMessage.DeviceStatusMessage(
|
||||
device=device.name,
|
||||
status=1,
|
||||
metadata={"pointID": ii, **metadata},
|
||||
).dumps(),
|
||||
)
|
||||
device.device_manager.producer.send(
|
||||
MessageEndpoints.device_read(device.name), bundle.dumps()
|
||||
)
|
||||
device.device_manager.producer.set_and_publish(
|
||||
MessageEndpoints.device_status(device.name),
|
||||
BECMessage.DeviceStatusMessage(
|
||||
device=device.name,
|
||||
status=0,
|
||||
metadata={"pointID": num_pos, **metadata},
|
||||
).dumps(),
|
||||
)
|
||||
print("done")
|
||||
|
||||
flyer = threading.Thread(target=produce_data, args=(self, metadata))
|
||||
flyer.start()
|
||||
|
||||
|
||||
class SynAxisOPAAS(Device, PositionerBase):
|
||||
|
241
ophyd_devices/sim/sim_xtreme.py
Normal file
241
ophyd_devices/sim/sim_xtreme.py
Normal file
@ -0,0 +1,241 @@
|
||||
import threading
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
from ophyd import Component as Cpt
|
||||
from ophyd import Device, Kind, Signal
|
||||
from ophyd.flyers import FlyerInterface
|
||||
from ophyd.ophydobj import Kind
|
||||
from ophyd.status import DeviceStatus, SubscriptionStatus
|
||||
from ophyd.utils import ReadOnlyError
|
||||
|
||||
|
||||
class SynSetpoint(Signal):
|
||||
def __init__(
|
||||
self,
|
||||
name,
|
||||
*,
|
||||
value=0,
|
||||
dtype=float,
|
||||
timestamp=None,
|
||||
parent=None,
|
||||
labels=None,
|
||||
kind=Kind.hinted,
|
||||
tolerance=None,
|
||||
rtolerance=None,
|
||||
metadata=None,
|
||||
cl=None,
|
||||
attr_name="",
|
||||
auto_monitor=False,
|
||||
):
|
||||
self._dtype = dtype
|
||||
if self._dtype == str and value == 0:
|
||||
value = ""
|
||||
super().__init__(
|
||||
name=name,
|
||||
value=value,
|
||||
timestamp=timestamp,
|
||||
parent=parent,
|
||||
labels=labels,
|
||||
kind=kind,
|
||||
tolerance=tolerance,
|
||||
rtolerance=rtolerance,
|
||||
metadata=metadata,
|
||||
cl=cl,
|
||||
attr_name=attr_name,
|
||||
)
|
||||
|
||||
def put(self, value, *, timestamp=None, force=False):
|
||||
old_val = self._readback
|
||||
self._readback = self._dtype(value)
|
||||
self._run_subs(
|
||||
sub_type="value",
|
||||
old_value=old_val,
|
||||
value=self._readback,
|
||||
timestamp=time.time(),
|
||||
)
|
||||
|
||||
def get(self):
|
||||
return self._readback
|
||||
|
||||
def describe(self):
|
||||
res = super().describe()
|
||||
# There should be only one key here, but for the sake of generality....
|
||||
for k in res:
|
||||
res[k]["precision"] = self.parent.precision
|
||||
return res
|
||||
|
||||
|
||||
class SynData(Signal):
|
||||
_default_sub = "value"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
name,
|
||||
value=0,
|
||||
timestamp=None,
|
||||
parent=None,
|
||||
labels=None,
|
||||
kind=Kind.hinted,
|
||||
tolerance=None,
|
||||
rtolerance=None,
|
||||
metadata=None,
|
||||
cl=None,
|
||||
attr_name="",
|
||||
auto_monitor=False,
|
||||
):
|
||||
super().__init__(
|
||||
name=name,
|
||||
value=value,
|
||||
timestamp=timestamp,
|
||||
parent=parent,
|
||||
labels=labels,
|
||||
kind=kind,
|
||||
tolerance=tolerance,
|
||||
rtolerance=rtolerance,
|
||||
metadata=metadata,
|
||||
cl=cl,
|
||||
attr_name=attr_name,
|
||||
)
|
||||
self._reset_data()
|
||||
|
||||
def _reset_data(self):
|
||||
self._readback = np.array([])
|
||||
|
||||
def get(self):
|
||||
return self._readback
|
||||
|
||||
def append(self, val: float):
|
||||
self._readback = np.append(self._readback, val)
|
||||
|
||||
def describe(self):
|
||||
res = super().describe()
|
||||
# There should be only one key here, but for the sake of
|
||||
# generality....
|
||||
for k in res:
|
||||
res[k]["precision"] = self.parent.precision
|
||||
return res
|
||||
|
||||
@property
|
||||
def timestamp(self):
|
||||
"""Timestamp of the readback value"""
|
||||
return time.time()
|
||||
|
||||
def put(self, value, *, timestamp=None, force=False):
|
||||
raise ReadOnlyError("The signal {} is readonly.".format(self.name))
|
||||
|
||||
def set(self, value, *, timestamp=None, force=False):
|
||||
raise ReadOnlyError("The signal {} is readonly.".format(self.name))
|
||||
|
||||
|
||||
class SynXtremeOtf(FlyerInterface, Device):
|
||||
"""
|
||||
PGM on-the-fly scan
|
||||
"""
|
||||
|
||||
SUB_VALUE = "value"
|
||||
SUB_FLYER = "flyer"
|
||||
_default_sub = SUB_VALUE
|
||||
|
||||
e1 = Cpt(SynSetpoint, kind=Kind.config)
|
||||
e2 = Cpt(SynSetpoint, kind=Kind.config)
|
||||
time = Cpt(SynSetpoint, kind=Kind.config)
|
||||
folder = Cpt(SynSetpoint, dtype=str, value="", kind=Kind.config)
|
||||
file = Cpt(SynSetpoint, dtype=str, value="", kind=Kind.config)
|
||||
acquire = Cpt(SynSetpoint, auto_monitor=True)
|
||||
edata = Cpt(SynData, kind=Kind.hinted, auto_monitor=True)
|
||||
data = Cpt(SynData, kind=Kind.hinted, auto_monitor=True)
|
||||
idata = Cpt(SynData, kind=Kind.hinted, auto_monitor=True)
|
||||
fdata = Cpt(SynData, kind=Kind.hinted, auto_monitor=True)
|
||||
count = Cpt(SynData, kind=Kind.omitted, auto_monitor=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self._start_time = 0
|
||||
self.acquire.subscribe(self._update_status, run=False)
|
||||
self.count.subscribe(self._update_data, run=False)
|
||||
self._data_event = threading.Event()
|
||||
self.precision = 3
|
||||
|
||||
def kickoff(self):
|
||||
self._start_time = time.time()
|
||||
self.acquire.put(1)
|
||||
status = DeviceStatus(self)
|
||||
status.set_finished()
|
||||
return status
|
||||
|
||||
def complete(self):
|
||||
def check_value(*, old_value, value, **kwargs):
|
||||
return old_value == 1 and value == 0
|
||||
|
||||
status = SubscriptionStatus(self.acquire, check_value, event_type=self.acquire.SUB_VALUE)
|
||||
return status
|
||||
|
||||
def collect(self):
|
||||
data = {"time": self._start_time, "data": {}, "timestamps": {}}
|
||||
for attr in ("edata", "data", "idata", "fdata"):
|
||||
obj = getattr(self, attr)
|
||||
data["data"][obj.name] = obj.get()
|
||||
data["timestamps"][obj.name] = obj.timestamp
|
||||
|
||||
return data
|
||||
|
||||
def describe_collect(self):
|
||||
desc = {}
|
||||
for attr in ("edata", "data", "idata", "fdata"):
|
||||
desc.update(getattr(self, attr).describe())
|
||||
return desc
|
||||
|
||||
def _update_status(self, *, old_value, value, **kwargs):
|
||||
if old_value == 1 and value == 0:
|
||||
self._done_acquiring()
|
||||
return
|
||||
if old_value == 0 and value == 1:
|
||||
threading.Thread(target=self._start_acquiring, daemon=True).start()
|
||||
|
||||
def _reset_data(self):
|
||||
for entry in ("edata", "data", "idata", "fdata"):
|
||||
getattr(self, entry)._reset_data()
|
||||
self.count._readback = 0
|
||||
self._data_event.clear()
|
||||
|
||||
def _populate_data(self):
|
||||
self._reset_data()
|
||||
while not self._data_event.is_set():
|
||||
for entry in ("edata", "data", "idata", "fdata"):
|
||||
getattr(self, entry).append(np.random.rand())
|
||||
self.count._readback = len(self.edata.get())
|
||||
self.count._run_subs(
|
||||
sub_type="value",
|
||||
old_value=self.count._readback - 1,
|
||||
value=self.count._readback,
|
||||
timestamp=time.time(),
|
||||
)
|
||||
time.sleep(0.2)
|
||||
self._data_event.clear()
|
||||
|
||||
def _start_acquiring(self):
|
||||
threading.Thread(target=self._populate_data, daemon=True).start()
|
||||
timeout_event = threading.Event()
|
||||
flag = timeout_event.wait(self.time.get())
|
||||
if not flag:
|
||||
self._data_event.set()
|
||||
self.acquire.put(0)
|
||||
|
||||
def _update_data(self, value, **kwargs):
|
||||
if value == 0:
|
||||
return
|
||||
data = self.collect()
|
||||
self._run_subs(sub_type=self.SUB_FLYER, value=data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
obj = SynXtremeOtf(name="otf")
|
||||
status = obj.time.set(4)
|
||||
status.wait()
|
||||
status = obj.kickoff()
|
||||
status.wait()
|
||||
while obj.acquire.get():
|
||||
time.sleep(0.2)
|
||||
print("done")
|
Reference in New Issue
Block a user