Upgraed DDC

This commit is contained in:
gac-x05la
2025-04-16 12:15:27 +02:00
parent 84bc0d692f
commit 5f5bf291a1
2 changed files with 165 additions and 143 deletions

View File

@@ -38,72 +38,72 @@ femto_mean_curr:
readOnly: true
softwareTrigger: false
# es1_roty:
# readoutPriority: monitored
# description: 'Test rotation stage'
# deviceClass: ophyd.EpicsMotor
# deviceConfig:
# prefix: X02DA-ES1-SMP1:ROTY
# deviceTags:
# - es1-sam
# onFailure: buffer
# enabled: true
# readOnly: false
# softwareTrigger: false
es1_roty:
readoutPriority: monitored
description: 'Test rotation stage'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: X02DA-ES1-SMP1:ROTY
deviceTags:
- es1-sam
onFailure: buffer
enabled: true
readOnly: false
softwareTrigger: false
# es1_ismc:
# description: 'Automation1 iSMC interface'
# deviceClass: tomcat_bec.devices.aa1Controller
# deviceConfig:
# prefix: 'X02DA-ES1-SMP1:CTRL:'
# deviceTags:
# - es1
# enabled: true
# onFailure: buffer
# readOnly: false
# readoutPriority: monitored
# softwareTrigger: false
es1_ismc:
description: 'Automation1 iSMC interface'
deviceClass: tomcat_bec.devices.aa1Controller
deviceConfig:
prefix: 'X02DA-ES1-SMP1:CTRL:'
deviceTags:
- es1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: false
# es1_tasks:
# description: 'Automation1 task management interface'
# deviceClass: tomcat_bec.devices.aa1Tasks
# deviceConfig:
# prefix: 'X02DA-ES1-SMP1:TASK:'
# deviceTags:
# - es1
# enabled: false
# onFailure: buffer
# readOnly: false
# readoutPriority: monitored
# softwareTrigger: false
es1_tasks:
description: 'Automation1 task management interface'
deviceClass: tomcat_bec.devices.aa1Tasks
deviceConfig:
prefix: 'X02DA-ES1-SMP1:TASK:'
deviceTags:
- es1
enabled: false
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: false
# es1_psod:
# description: 'AA1 PSO output interface (trigger)'
# deviceClass: tomcat_bec.devices.aa1AxisPsoDistance
# deviceConfig:
# prefix: 'X02DA-ES1-SMP1:ROTY:PSO:'
# deviceTags:
# - es1
# enabled: true
# onFailure: buffer
# readOnly: false
# readoutPriority: monitored
# softwareTrigger: true
es1_psod:
description: 'AA1 PSO output interface (trigger)'
deviceClass: tomcat_bec.devices.aa1AxisPsoDistance
deviceConfig:
prefix: 'X02DA-ES1-SMP1:ROTY:PSO:'
deviceTags:
- es1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: true
# es1_ddaq:
# description: 'Automation1 position recording interface'
# deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection
# deviceConfig:
# prefix: 'X02DA-ES1-SMP1:ROTY:DDC:'
# deviceTags:
# - es1
# enabled: true
# onFailure: buffer
# readOnly: false
# readoutPriority: monitored
# softwareTrigger: false
es1_ddaq:
description: 'Automation1 position recording interface'
deviceClass: tomcat_bec.devices.aa1AxisDriveDataCollection
deviceConfig:
prefix: 'X02DA-ES1-SMP1:ROTY:DDC:'
deviceTags:
- es1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: false
#camera:
@@ -119,25 +119,25 @@ femto_mean_curr:
# readoutPriority: monitored
# softwareTrigger: true
gfcam:
description: GigaFrost camera client
deviceClass: tomcat_bec.devices.GigaFrostCamera
deviceConfig:
prefix: 'X02DA-CAM-GF2:'
backend_url: 'http://sls-daq-001:8080'
auto_soft_enable: true
std_daq_live: 'tcp://129.129.95.111:20000'
std_daq_ws: 'ws://129.129.95.111:8080'
std_daq_rest: 'http://129.129.95.111:5000'
deviceTags:
- camera
- trigger
- gfcam
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: monitored
softwareTrigger: true
# gfcam:
# description: GigaFrost camera client
# deviceClass: tomcat_bec.devices.GigaFrostCamera
# deviceConfig:
# prefix: 'X02DA-CAM-GF2:'
# backend_url: 'http://sls-daq-001:8080'
# auto_soft_enable: true
# std_daq_live: 'tcp://129.129.95.111:20000'
# std_daq_ws: 'ws://129.129.95.111:8080'
# std_daq_rest: 'http://129.129.95.111:5000'
# deviceTags:
# - camera
# - trigger
# - gfcam
# enabled: true
# onFailure: buffer
# readOnly: false
# readoutPriority: monitored
# softwareTrigger: true
# gfdaq:
# description: GigaFrost stdDAQ client

View File

@@ -8,68 +8,17 @@ drive data collection (DDC) interface.
import time
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_devices.interfaces.base_classes.psi_detector_base import PSIDetectorBase as PSIDeviceBase
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
CustomDetectorMixin as CustomDeviceMixin,
)
from ophyd_devices.interfaces.base_classes.psi_device_base import PSIDeviceBase
from bec_lib import bec_logger
logger = bec_logger.logger
class AerotechDriveDataCollectionMixin(CustomDeviceMixin):
"""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):
class aa1AxisDriveDataCollection(PSIDeviceBase, Device):
"""Axis data collection
This class provides convenience wrappers around the Aerotech API's axis
@@ -88,9 +37,10 @@ class aa1AxisDriveDataCollection(PSIDeviceBase):
...
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,
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)
_buffer1 = Component(EpicsSignalRO, "BUFFER1", auto_monitor=True, kind=Kind.normal)
custom_prepare_cls = AerotechDriveDataCollectionMixin
USER_ACCESS = ["configure", "reset"]
USER_ACCESS = ["configure", "reset", "arm", "disarm"]
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:
"""Configure data capture
@@ -128,21 +101,68 @@ class aa1AxisDriveDataCollection(PSIDeviceBase):
if "num_points_total" in d:
self.npoints.set(d["num_points_total"]).wait()
if "ddc_trigger" in d:
self._trigger.set(d['ddc_trigger']).wait()
self._trigger.set(d["ddc_trigger"]).wait()
if "ddc_source0" in d:
self._input0.set(d['ddc_source0']).wait()
self._input0.set(d["ddc_source0"]).wait()
if "ddc_source1" in d:
self._input1.set(d['ddc_source1']).wait()
self._input1.set(d["ddc_source1"]).wait()
# Reset incremental readback
self._switch.set("ResetRB", settle_time=0.1).wait()
new = self.read_configuration()
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"""
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):
"""Reset incremental readback"""
self._switch.set("ResetRB", settle_time=0.1).wait()
@@ -164,20 +184,22 @@ class aa1AxisDriveDataCollection(PSIDeviceBase):
timestamp_ = timestamp
return result
status = None
if index == 0:
status = SubscriptionStatus(self._readstatus0, neg_edge, settle_time=0.5)
self._readback0.set(1).wait()
elif index == 1:
status = SubscriptionStatus(self._readstatus1, neg_edge, settle_time=0.5)
self._readback1.set(1).wait()
else:
raise RuntimeError(f"Unsupported drive data collection channel: {index}")
# Start asynchronous readback
status.wait()
return status
def describe_collect(self) -> OrderedDict:
"""Describes collected array format according to JSONschema
"""
"""Describes collected array format according to JSONschema"""
ret = OrderedDict()
ret["buffer0"] = {
"source": "internal",