Upgraed DDC
This commit is contained in:
@@ -38,72 +38,72 @@ femto_mean_curr:
|
|||||||
readOnly: true
|
readOnly: true
|
||||||
softwareTrigger: false
|
softwareTrigger: false
|
||||||
|
|
||||||
# es1_roty:
|
es1_roty:
|
||||||
# readoutPriority: monitored
|
readoutPriority: monitored
|
||||||
# description: 'Test rotation stage'
|
description: 'Test rotation stage'
|
||||||
# deviceClass: ophyd.EpicsMotor
|
deviceClass: ophyd.EpicsMotor
|
||||||
# deviceConfig:
|
deviceConfig:
|
||||||
# prefix: X02DA-ES1-SMP1:ROTY
|
prefix: X02DA-ES1-SMP1:ROTY
|
||||||
# deviceTags:
|
deviceTags:
|
||||||
# - es1-sam
|
- es1-sam
|
||||||
# onFailure: buffer
|
onFailure: buffer
|
||||||
# enabled: true
|
enabled: true
|
||||||
# readOnly: false
|
readOnly: false
|
||||||
# softwareTrigger: false
|
softwareTrigger: false
|
||||||
|
|
||||||
# es1_ismc:
|
es1_ismc:
|
||||||
# description: 'Automation1 iSMC interface'
|
description: 'Automation1 iSMC interface'
|
||||||
# deviceClass: tomcat_bec.devices.aa1Controller
|
deviceClass: tomcat_bec.devices.aa1Controller
|
||||||
# deviceConfig:
|
deviceConfig:
|
||||||
# prefix: 'X02DA-ES1-SMP1:CTRL:'
|
prefix: 'X02DA-ES1-SMP1:CTRL:'
|
||||||
# deviceTags:
|
deviceTags:
|
||||||
# - es1
|
- es1
|
||||||
# enabled: true
|
enabled: true
|
||||||
# onFailure: buffer
|
onFailure: buffer
|
||||||
# readOnly: false
|
readOnly: false
|
||||||
# readoutPriority: monitored
|
readoutPriority: monitored
|
||||||
# softwareTrigger: false
|
softwareTrigger: false
|
||||||
|
|
||||||
# es1_tasks:
|
es1_tasks:
|
||||||
# description: 'Automation1 task management interface'
|
description: 'Automation1 task management interface'
|
||||||
# deviceClass: tomcat_bec.devices.aa1Tasks
|
deviceClass: tomcat_bec.devices.aa1Tasks
|
||||||
# deviceConfig:
|
deviceConfig:
|
||||||
# prefix: 'X02DA-ES1-SMP1:TASK:'
|
prefix: 'X02DA-ES1-SMP1:TASK:'
|
||||||
# deviceTags:
|
deviceTags:
|
||||||
# - es1
|
- es1
|
||||||
# enabled: false
|
enabled: false
|
||||||
# onFailure: buffer
|
onFailure: buffer
|
||||||
# readOnly: false
|
readOnly: false
|
||||||
# readoutPriority: monitored
|
readoutPriority: monitored
|
||||||
# softwareTrigger: false
|
softwareTrigger: false
|
||||||
|
|
||||||
|
|
||||||
# es1_psod:
|
es1_psod:
|
||||||
# description: 'AA1 PSO output interface (trigger)'
|
description: 'AA1 PSO output interface (trigger)'
|
||||||
# deviceClass: tomcat_bec.devices.aa1AxisPsoDistance
|
deviceClass: tomcat_bec.devices.aa1AxisPsoDistance
|
||||||
# deviceConfig:
|
deviceConfig:
|
||||||
# prefix: 'X02DA-ES1-SMP1:ROTY:PSO:'
|
prefix: 'X02DA-ES1-SMP1:ROTY:PSO:'
|
||||||
# deviceTags:
|
deviceTags:
|
||||||
# - es1
|
- es1
|
||||||
# enabled: true
|
enabled: true
|
||||||
# onFailure: buffer
|
onFailure: buffer
|
||||||
# readOnly: false
|
readOnly: false
|
||||||
# readoutPriority: monitored
|
readoutPriority: monitored
|
||||||
# softwareTrigger: true
|
softwareTrigger: true
|
||||||
|
|
||||||
|
|
||||||
# es1_ddaq:
|
es1_ddaq:
|
||||||
# description: 'Automation1 position recording interface'
|
description: 'Automation1 position recording interface'
|
||||||
# deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection
|
deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection
|
||||||
# deviceConfig:
|
deviceConfig:
|
||||||
# prefix: 'X02DA-ES1-SMP1:ROTY:DDC:'
|
prefix: 'X02DA-ES1-SMP1:ROTY:DDC:'
|
||||||
# deviceTags:
|
deviceTags:
|
||||||
# - es1
|
- es1
|
||||||
# enabled: true
|
enabled: true
|
||||||
# onFailure: buffer
|
onFailure: buffer
|
||||||
# readOnly: false
|
readOnly: false
|
||||||
# readoutPriority: monitored
|
readoutPriority: monitored
|
||||||
# softwareTrigger: false
|
softwareTrigger: false
|
||||||
|
|
||||||
|
|
||||||
#camera:
|
#camera:
|
||||||
@@ -119,25 +119,25 @@ femto_mean_curr:
|
|||||||
# readoutPriority: monitored
|
# readoutPriority: monitored
|
||||||
# softwareTrigger: true
|
# softwareTrigger: true
|
||||||
|
|
||||||
gfcam:
|
# gfcam:
|
||||||
description: GigaFrost camera client
|
# description: GigaFrost camera client
|
||||||
deviceClass: tomcat_bec.devices.GigaFrostCamera
|
# deviceClass: tomcat_bec.devices.GigaFrostCamera
|
||||||
deviceConfig:
|
# deviceConfig:
|
||||||
prefix: 'X02DA-CAM-GF2:'
|
# prefix: 'X02DA-CAM-GF2:'
|
||||||
backend_url: 'http://sls-daq-001:8080'
|
# backend_url: 'http://sls-daq-001:8080'
|
||||||
auto_soft_enable: true
|
# auto_soft_enable: true
|
||||||
std_daq_live: 'tcp://129.129.95.111:20000'
|
# std_daq_live: 'tcp://129.129.95.111:20000'
|
||||||
std_daq_ws: 'ws://129.129.95.111:8080'
|
# std_daq_ws: 'ws://129.129.95.111:8080'
|
||||||
std_daq_rest: 'http://129.129.95.111:5000'
|
# std_daq_rest: 'http://129.129.95.111:5000'
|
||||||
deviceTags:
|
# deviceTags:
|
||||||
- camera
|
# - camera
|
||||||
- trigger
|
# - trigger
|
||||||
- gfcam
|
# - gfcam
|
||||||
enabled: true
|
# enabled: true
|
||||||
onFailure: buffer
|
# onFailure: buffer
|
||||||
readOnly: false
|
# readOnly: false
|
||||||
readoutPriority: monitored
|
# readoutPriority: monitored
|
||||||
softwareTrigger: true
|
# softwareTrigger: true
|
||||||
|
|
||||||
# gfdaq:
|
# gfdaq:
|
||||||
# description: GigaFrost stdDAQ client
|
# description: GigaFrost stdDAQ client
|
||||||
|
|||||||
@@ -8,68 +8,17 @@ drive data collection (DDC) interface.
|
|||||||
import time
|
import time
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
from ophyd import Component, EpicsSignal, EpicsSignalRO, Kind
|
from ophyd import Device, Component, EpicsSignal, EpicsSignalRO, Kind
|
||||||
from ophyd.status import SubscriptionStatus
|
from ophyd.status import SubscriptionStatus
|
||||||
|
|
||||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase as PSIDeviceBase
|
from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase
|
||||||
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
|
|
||||||
CustomDetectorMixin as CustomDeviceMixin,
|
|
||||||
)
|
|
||||||
from bec_lib import bec_logger
|
from bec_lib import bec_logger
|
||||||
|
|
||||||
logger = bec_logger.logger
|
logger = bec_logger.logger
|
||||||
|
|
||||||
|
|
||||||
class AerotechDriveDataCollectionMixin(CustomDeviceMixin):
|
class aa1AxisDriveDataCollection(PSIDeviceBase, Device):
|
||||||
"""Mixin class for self-configuration and staging
|
|
||||||
|
|
||||||
NOTE: scripted scans start drive data collection internally
|
|
||||||
"""
|
|
||||||
# parent : aa1Tasks
|
|
||||||
def on_stage(self) -> None:
|
|
||||||
"""Configuration and staging"""
|
|
||||||
|
|
||||||
# Fish out configuration from scaninfo (does not need to be full configuration)
|
|
||||||
d = {}
|
|
||||||
if "kwargs" in self.parent.scaninfo.scan_msg.info:
|
|
||||||
scanargs = self.parent.scaninfo.scan_msg.info["kwargs"]
|
|
||||||
# NOTE: Scans don't have to fully configure the device
|
|
||||||
if "ddc_trigger" in scanargs:
|
|
||||||
d["ddc_trigger"] = scanargs["ddc_trigger"]
|
|
||||||
if "ddc_num_points" in scanargs:
|
|
||||||
d["num_points_total"] = scanargs["ddc_num_points"]
|
|
||||||
else:
|
|
||||||
# Try to figure out number of points
|
|
||||||
num_points = 1
|
|
||||||
points_valid = False
|
|
||||||
if "steps" in scanargs and scanargs['steps'] is not None:
|
|
||||||
num_points *= scanargs["steps"]
|
|
||||||
points_valid = True
|
|
||||||
elif "exp_burst" in scanargs and scanargs['exp_burst'] is not None:
|
|
||||||
num_points *= scanargs["exp_burst"]
|
|
||||||
points_valid = True
|
|
||||||
elif "repeats" in scanargs and scanargs['repeats'] is not None:
|
|
||||||
num_points *= scanargs["repeats"]
|
|
||||||
points_valid = True
|
|
||||||
if points_valid:
|
|
||||||
d["num_points_total"] = num_points
|
|
||||||
|
|
||||||
# Perform bluesky-style configuration
|
|
||||||
if len(d) > 0:
|
|
||||||
logger.warning(f"[{self.parent.name}] Configuring with:\n{d}")
|
|
||||||
self.parent.configure(d=d)
|
|
||||||
|
|
||||||
# Stage the data collection if not in internally launced mode
|
|
||||||
# NOTE: Scripted scans start acquiring from the scrits
|
|
||||||
if self.parent.scaninfo.scan_type not in ("script", "scripted"):
|
|
||||||
self.parent.bluestage()
|
|
||||||
|
|
||||||
def on_unstage(self):
|
|
||||||
"""Standard bluesky unstage"""
|
|
||||||
self.parent._switch.set("Stop", settle_time=0.2).wait()
|
|
||||||
|
|
||||||
|
|
||||||
class aa1AxisDriveDataCollection(PSIDeviceBase):
|
|
||||||
"""Axis data collection
|
"""Axis data collection
|
||||||
|
|
||||||
This class provides convenience wrappers around the Aerotech API's axis
|
This class provides convenience wrappers around the Aerotech API's axis
|
||||||
@@ -88,9 +37,10 @@ class aa1AxisDriveDataCollection(PSIDeviceBase):
|
|||||||
...
|
...
|
||||||
ret = yield from ddc.collect()
|
ret = yield from ddc.collect()
|
||||||
|
|
||||||
|
NOTE: scripted scans start drive data collection internally
|
||||||
|
|
||||||
NOTE: Expected behavior is that the device is disabled when not in use,
|
NOTE: Expected behavior is that the device is disabled when not in use,
|
||||||
i.e. there's avtive enable/disable management.
|
i.e. there's active enable/disable management.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
@@ -111,8 +61,31 @@ class aa1AxisDriveDataCollection(PSIDeviceBase):
|
|||||||
_buffer0 = Component(EpicsSignalRO, "BUFFER0", auto_monitor=True, kind=Kind.normal)
|
_buffer0 = Component(EpicsSignalRO, "BUFFER0", auto_monitor=True, kind=Kind.normal)
|
||||||
_buffer1 = Component(EpicsSignalRO, "BUFFER1", auto_monitor=True, kind=Kind.normal)
|
_buffer1 = Component(EpicsSignalRO, "BUFFER1", auto_monitor=True, kind=Kind.normal)
|
||||||
|
|
||||||
custom_prepare_cls = AerotechDriveDataCollectionMixin
|
USER_ACCESS = ["configure", "reset", "arm", "disarm"]
|
||||||
USER_ACCESS = ["configure", "reset"]
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
prefix="",
|
||||||
|
*,
|
||||||
|
name,
|
||||||
|
kind=None,
|
||||||
|
read_attrs=None,
|
||||||
|
configuration_attrs=None,
|
||||||
|
parent=None,
|
||||||
|
scan_info=None,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
# super() will call the mixin class
|
||||||
|
super().__init__(
|
||||||
|
prefix=prefix,
|
||||||
|
name=name,
|
||||||
|
kind=kind,
|
||||||
|
read_attrs=read_attrs,
|
||||||
|
configuration_attrs=configuration_attrs,
|
||||||
|
parent=parent,
|
||||||
|
scan_info=scan_info,
|
||||||
|
**kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
def configure(self, d: dict = None) -> tuple:
|
def configure(self, d: dict = None) -> tuple:
|
||||||
"""Configure data capture
|
"""Configure data capture
|
||||||
@@ -128,21 +101,68 @@ class aa1AxisDriveDataCollection(PSIDeviceBase):
|
|||||||
if "num_points_total" in d:
|
if "num_points_total" in d:
|
||||||
self.npoints.set(d["num_points_total"]).wait()
|
self.npoints.set(d["num_points_total"]).wait()
|
||||||
if "ddc_trigger" in d:
|
if "ddc_trigger" in d:
|
||||||
self._trigger.set(d['ddc_trigger']).wait()
|
self._trigger.set(d["ddc_trigger"]).wait()
|
||||||
if "ddc_source0" in d:
|
if "ddc_source0" in d:
|
||||||
self._input0.set(d['ddc_source0']).wait()
|
self._input0.set(d["ddc_source0"]).wait()
|
||||||
if "ddc_source1" in d:
|
if "ddc_source1" in d:
|
||||||
self._input1.set(d['ddc_source1']).wait()
|
self._input1.set(d["ddc_source1"]).wait()
|
||||||
|
|
||||||
# Reset incremental readback
|
# Reset incremental readback
|
||||||
self._switch.set("ResetRB", settle_time=0.1).wait()
|
self._switch.set("ResetRB", settle_time=0.1).wait()
|
||||||
new = self.read_configuration()
|
new = self.read_configuration()
|
||||||
return (old, new)
|
return (old, new)
|
||||||
|
|
||||||
def bluestage(self) -> None:
|
def on_stage(self) -> None:
|
||||||
|
"""Configuration and staging"""
|
||||||
|
# Fish out configuration from scaninfo (does not need to be full configuration)
|
||||||
|
d = {}
|
||||||
|
if "kwargs" in self.scaninfo.scan_msg.info:
|
||||||
|
scanargs = self.scaninfo.scan_msg.info["kwargs"]
|
||||||
|
# NOTE: Scans don't have to fully configure the device
|
||||||
|
if "ddc_trigger" in scanargs:
|
||||||
|
d["ddc_trigger"] = scanargs["ddc_trigger"]
|
||||||
|
if "ddc_num_points" in scanargs:
|
||||||
|
d["num_points_total"] = scanargs["ddc_num_points"]
|
||||||
|
else:
|
||||||
|
# Try to figure out number of points
|
||||||
|
num_points = 1
|
||||||
|
points_valid = False
|
||||||
|
if "steps" in scanargs and scanargs["steps"] is not None:
|
||||||
|
num_points *= scanargs["steps"]
|
||||||
|
points_valid = True
|
||||||
|
elif "exp_burst" in scanargs and scanargs["exp_burst"] is not None:
|
||||||
|
num_points *= scanargs["exp_burst"]
|
||||||
|
points_valid = True
|
||||||
|
elif "repeats" in scanargs and scanargs["repeats"] is not None:
|
||||||
|
num_points *= scanargs["repeats"]
|
||||||
|
points_valid = True
|
||||||
|
if points_valid:
|
||||||
|
d["num_points_total"] = num_points
|
||||||
|
|
||||||
|
# Perform bluesky-style configuration
|
||||||
|
if len(d) > 0:
|
||||||
|
logger.warning(f"[{self.name}] Configuring with:\n{d}")
|
||||||
|
self.configure(d=d)
|
||||||
|
|
||||||
|
# Stage the data collection if not in internally launced mode
|
||||||
|
# NOTE: Scripted scans start acquiring from the scrits
|
||||||
|
if self.scaninfo.scan_type not in ("script", "scripted"):
|
||||||
|
self.arm()
|
||||||
|
# Reset readback
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def on_unstage(self):
|
||||||
|
"""Standard bluesky unstage"""
|
||||||
|
self.disarm()
|
||||||
|
|
||||||
|
def arm(self) -> None:
|
||||||
"""Bluesky-style stage"""
|
"""Bluesky-style stage"""
|
||||||
self._switch.set("Start", settle_time=0.2).wait()
|
self._switch.set("Start", settle_time=0.2).wait()
|
||||||
|
|
||||||
|
def disarm(self):
|
||||||
|
"""Standard bluesky unstage"""
|
||||||
|
self._switch.set("Stop", settle_time=0.2).wait()
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"""Reset incremental readback"""
|
"""Reset incremental readback"""
|
||||||
self._switch.set("ResetRB", settle_time=0.1).wait()
|
self._switch.set("ResetRB", settle_time=0.1).wait()
|
||||||
@@ -164,20 +184,22 @@ class aa1AxisDriveDataCollection(PSIDeviceBase):
|
|||||||
timestamp_ = timestamp
|
timestamp_ = timestamp
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
status = None
|
||||||
if index == 0:
|
if index == 0:
|
||||||
status = SubscriptionStatus(self._readstatus0, neg_edge, settle_time=0.5)
|
status = SubscriptionStatus(self._readstatus0, neg_edge, settle_time=0.5)
|
||||||
self._readback0.set(1).wait()
|
self._readback0.set(1).wait()
|
||||||
elif index == 1:
|
elif index == 1:
|
||||||
status = SubscriptionStatus(self._readstatus1, neg_edge, settle_time=0.5)
|
status = SubscriptionStatus(self._readstatus1, neg_edge, settle_time=0.5)
|
||||||
self._readback1.set(1).wait()
|
self._readback1.set(1).wait()
|
||||||
|
else:
|
||||||
|
raise RuntimeError(f"Unsupported drive data collection channel: {index}")
|
||||||
|
|
||||||
# Start asynchronous readback
|
# Start asynchronous readback
|
||||||
status.wait()
|
status.wait()
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def describe_collect(self) -> OrderedDict:
|
def describe_collect(self) -> OrderedDict:
|
||||||
"""Describes collected array format according to JSONschema
|
"""Describes collected array format according to JSONschema"""
|
||||||
"""
|
|
||||||
ret = OrderedDict()
|
ret = OrderedDict()
|
||||||
ret["buffer0"] = {
|
ret["buffer0"] = {
|
||||||
"source": "internal",
|
"source": "internal",
|
||||||
|
|||||||
Reference in New Issue
Block a user