Aerotech scans work nicely

This commit is contained in:
Unknown MX Person
2024-09-20 14:38:04 +02:00
parent 14ca9bd74a
commit b0703552f2
5 changed files with 77 additions and 30 deletions
@@ -345,7 +345,7 @@ omega:
abr: abr:
description: Aerotech ABR motion system description: Aerotech ABR motion system
deviceClass: pxiii_bec.devices.AerotechAbrStage deviceClass: pxiii_bec.devices.AerotechAbrStage
deviceConfig: {prefix: X06DA-ES} deviceConfig: {prefix: 'X06DA-ES'}
onFailure: buffer onFailure: buffer
enabled: true enabled: true
readoutPriority: monitored readoutPriority: monitored
+21 -1
View File
@@ -81,7 +81,7 @@ Examples
import time import time
import math import math
from ophyd import Component, Device, EpicsSignal, EpicsSignalRO, Kind from ophyd import Component, Device, EpicsSignal, EpicsSignalRO, Kind, SignalRO
from ophyd.status import MoveStatus, SubscriptionStatus, DeviceStatus from ophyd.status import MoveStatus, SubscriptionStatus, DeviceStatus
@@ -185,6 +185,12 @@ class AerotechAbrStage(Device):
_var_9 = Component(EpicsSignal, "-PSO:VAR-9", put_complete=True, kind=Kind.omitted) _var_9 = Component(EpicsSignal, "-PSO:VAR-9", put_complete=True, kind=Kind.omitted)
_var_10 = Component(EpicsSignal, "-PSO:VAR-10", put_complete=True, kind=Kind.omitted) _var_10 = Component(EpicsSignal, "-PSO:VAR-10", put_complete=True, kind=Kind.omitted)
# Task status PVs (programs always run on task 1)
task1 = Component(EpicsSignalRO, "-AERO:TSK1-DONE", auto_monitor=True, kind=Kind.hinted)
task2 = Component(EpicsSignalRO, "-AERO:TSK2-DONE", auto_monitor=True)
task3 = Component(EpicsSignalRO, "-AERO:TSK3-DONE", auto_monitor=True)
task4 = Component(EpicsSignalRO, "-AERO:TSK4-DONE", auto_monitor=True)
# A few PVs still needed from grid # A few PVs still needed from grid
raster_scan_done = Component(EpicsSignal, "-GRD:SCAN-DONE", kind=Kind.config) raster_scan_done = Component(EpicsSignal, "-GRD:SCAN-DONE", kind=Kind.config)
raster_num_rows = Component(EpicsSignal, "-GRD:ROW-DONE", kind=Kind.config) raster_num_rows = Component(EpicsSignal, "-GRD:ROW-DONE", kind=Kind.config)
@@ -255,6 +261,20 @@ class AerotechAbrStage(Device):
""" """
return self.raster.complete() return self.raster.complete()
def complete(self, timeout=None) -> SubscriptionStatus:
""" Waits for task execution
NOTE: Original complete was raster scanner complete...
"""
# Define wait until the busy flag goes down (excluding initial update)
def isIdle(*args, old_value, value, timestamp, **kwargs):
return bool(value==1)
# Subscribe and wait for update
status = SubscriptionStatus(self.task1, isIdle, timeout=timeout, settle_time=0.5)
return status
def reset(self, settle_time=0.1): def reset(self, settle_time=0.1):
self.scan_command.set(CMD_NONE, settle_time=settle_time).wait() self.scan_command.set(CMD_NONE, settle_time=settle_time).wait()
self.set_axis_mode("direct", settle_time=settle_time) self.set_axis_mode("direct", settle_time=settle_time)
+24 -5
View File
@@ -25,10 +25,29 @@ MEASURING_MODE = 1
class A3200Axis(PVPositioner): class A3200Axis(PVPositioner):
"""Positioner wrapper for motors on the Aerotech A3200 controller. As the """ Positioner wrapper for A3200 axes
IOC does not provide a motor record, this class simply wraps axes into
a standard Ophyd positioner for the BEC. It also has some additional
functionality for diagnostics. Positioner wrapper for motors on the Aerotech A3200 controller. As the IOC
does not provide a motor record, this class simply wraps axes into a
standard Ophyd positioner for the BEC. It also has some additional
functionality for error checking and diagnostics.
Examples
--------
omega = A3200Axis('X06DA-ES-DF1:OMEGA', base_pv='X06DA-ES')
class abr(Device):
omega = Component(A3200Axis, '-DF1:OMEGA')
gmx = Component(A3200Axis, '-DF1:GMX')
gmy = Component(A3200Axis, '-DF1:GMY')
Parameters
----------
prefix : str
Axis PV name root.
base_pv : str (situational)
IOC PV name root, i.e. X06DA-ES if standalone class.
""" """
USER_ACCESS = ['omove'] USER_ACCESS = ['omove']
@@ -74,7 +93,7 @@ class A3200Axis(PVPositioner):
""" Patching the parent's PVs into the axis class to check for direct/locked mode""" """ Patching the parent's PVs into the axis class to check for direct/locked mode"""
if parent is None: if parent is None:
def maybe_add_prefix(self, instance, kw, suffix): def maybe_add_prefix(self, instance, kw, suffix):
""" Patched not to enforce parent prefix""" """ Patched not to enforce parent prefix when no parent"""
if kw in self.add_prefix: if kw in self.add_prefix:
return suffix return suffix
return suffix return suffix
+1 -1
View File
@@ -1 +1 @@
from .mx_measurements import MeasureFastOmega from .mx_measurements import MeasureFastOmega, MeasureStandardWedge, MeasureVerticalLine, MeasureScanSlits, MeasureRasterSimple, MeasureScreening, MeasureRepeatedOscillation, MeasureMSOX, MeasureGrid
+29 -21
View File
@@ -45,12 +45,19 @@ class AbrCmd:
class AerotechFlyscanBase(AsyncFlyScanBase): class AerotechFlyscanBase(AsyncFlyScanBase):
""" Base class for MX flyscans """ Base class for MX flyscans
Low-level base class for standard scans at the PX beamlines at SLS. Theese scans use the Low-level base class for standard scans at the PX beamlines at SLS. Theese
A3200 rotation stage and the actual experiment is performed using an AeroBasic script scans use the A3200 rotation stage and the actual experiment is performed
controlled via global variables. using an AeroBasic script controlled via global variables. The base class
has some basic safety features like checking status then sets globals and
fires off the scan. Implementations can choose to set the corresponding
configurations in child classes or pass it as command line parameters.
IMPORTANT: The AeroBasic scripts take care of the PSO configuration. IMPORTANT: The AeroBasic scripts take care of the PSO configuration.
Parameters:
-----------
abr_complete : bool
Wait for the launched ABR task to complete.
""" """
scan_report_hint = "table" scan_report_hint = "table"
arg_input = {} arg_input = {}
@@ -59,7 +66,6 @@ class AerotechFlyscanBase(AsyncFlyScanBase):
# Aerotech stage config # Aerotech stage config
abr_command = None abr_command = None
abr_globals = {} abr_globals = {}
abr_reset = False
abr_raster_reset =False abr_raster_reset =False
abr_complete = False abr_complete = False
abr_timeout = None abr_timeout = None
@@ -76,8 +82,6 @@ class AerotechFlyscanBase(AsyncFlyScanBase):
self.abr_command = self.caller_kwargs.get("abr_command") self.abr_command = self.caller_kwargs.get("abr_command")
if "abr_globals" in self.caller_kwargs: if "abr_globals" in self.caller_kwargs:
self.abr_globals = self.caller_kwargs.get("abr_globals") self.abr_globals = self.caller_kwargs.get("abr_globals")
if "abr_reset" in self.caller_kwargs:
self.abr_reset = self.caller_kwargs.get("abr_reset")
if "abr_raster_reset" in self.caller_kwargs: if "abr_raster_reset" in self.caller_kwargs:
self.abr_raster_reset = self.caller_kwargs.get("abr_raster_reset") self.abr_raster_reset = self.caller_kwargs.get("abr_raster_reset")
if "abr_complete" in self.caller_kwargs: if "abr_complete" in self.caller_kwargs:
@@ -86,17 +90,19 @@ class AerotechFlyscanBase(AsyncFlyScanBase):
self.abr_timeout = self.caller_kwargs.get("abr_timeout") self.abr_timeout = self.caller_kwargs.get("abr_timeout")
def pre_scan(self): def pre_scan(self):
# ToDo: Move roughly to start position # TODO: Move roughly to start position???
# ABR status checking
stat = yield from self.stubs.send_rpc_and_wait("abr","status.get") stat = yield from self.stubs.send_rpc_and_wait("abr","status.get")
if stat not in (0, "OK"): if stat not in (0, "OK"):
raise RuntimeError("Aerotech ABR seems to be in error state {stat}, please reset") raise RuntimeError("Aerotech ABR seems to be in error state {stat}, please reset")
task = yield from self.stubs.send_rpc_and_wait("abr","task1.get")
# From what I got values are copied to local vars at the start of scan,
# so only kickoff should be forbidden.
if task not in (1, "OK"):
raise RuntimeError("Aerotech ABR task #1 seems to busy")
if self.abr_reset: # Reset the raster scan engine
yield from self.stubs.send_rpc_and_wait("abr", "reset")
# Reset the scan engine
if self.abr_reset:
yield from self.stubs.send_rpc_and_wait("abr", "reset")
if self.abr_raster_reset: if self.abr_raster_reset:
yield from self.stubs.command("abr", "raster_scan_done.set", 0) yield from self.stubs.command("abr", "raster_scan_done.set", 0)
@@ -123,9 +129,13 @@ class AerotechFlyscanBase(AsyncFlyScanBase):
logger.info("Measurement launched on the ABR stage...") logger.info("Measurement launched on the ABR stage...")
# Wait for grid scanner to finish # Wait for grid scanner to finish
if self.abr_complete and self.abr_timeout is not None: if self.abr_complete:
st = yield from self.stubs.send_rpc_and_wait("abr", "complete", self.abr_timeout) if self.abr_timeout is not None:
st.wait() st = yield from self.stubs.send_rpc_and_wait("abr", "complete", self.abr_timeout)
st.wait()
else:
st = yield from self.stubs.send_rpc_and_wait("abr", "complete")
st.wait()
def cleanup(self): def cleanup(self):
"""Set scan progress to 1 to finish the scan""" """Set scan progress to 1 to finish the scan"""
@@ -175,6 +185,7 @@ class MeasureStandardWedge(AerotechFlyscanBase):
"var_3" : self.scan_move_time, "var_3" : self.scan_move_time,
"var_4" : self.scan_ready_rate, "var_4" : self.scan_ready_rate,
} }
logger.info(self.abr_globals)
# Call base class # Call base class
super().__init__(parameter=parameter, **kwargs) super().__init__(parameter=parameter, **kwargs)
@@ -204,7 +215,7 @@ class MeasureVerticalLine(AerotechFlyscanBase):
exp_time : float exp_time : float
Eeffective exposure time per step [s] Eeffective exposure time per step [s]
""" """
scan_name = "measure_vline" scan_name = "vertical_line"
required_kwargs = ["exp_time", "range_y", "steps_y"] required_kwargs = ["exp_time", "range_y", "steps_y"]
def __init__(self, *args, parameter: dict = None, **kwargs): def __init__(self, *args, parameter: dict = None, **kwargs):
@@ -515,6 +526,7 @@ class MeasureFastOmega(AerotechFlyscanBase):
"var_3" : self.scan_move_time, "var_3" : self.scan_move_time,
"var_4" : self.scan_acceleration, "var_4" : self.scan_acceleration,
} }
logger.info(self.abr_globals)
# Call base class # Call base class
super().__init__(parameter=parameter, **kwargs) super().__init__(parameter=parameter, **kwargs)
@@ -750,7 +762,6 @@ class MeasureMSOX(AerotechFlyscanBase):
self.scan_stepnum = parameter['kwargs'].get("steps", 1) self.scan_stepnum = parameter['kwargs'].get("steps", 1)
self.scan_settle_time = parameter['kwargs'].get("settle_time", 0) self.scan_settle_time = parameter['kwargs'].get("settle_time", 0)
self.abr_command = AbrCmd.MSOX self.abr_command = AbrCmd.MSOX
self.abr_reset = True
self.abr_globals = { self.abr_globals = {
"var_1" : self.scan_start, "var_1" : self.scan_start,
"var_2" : self.scan_range, "var_2" : self.scan_range,
@@ -814,7 +825,6 @@ class MeasureGrid(AerotechFlyscanBase):
self.axis = ['abr.omega', 'abr.gmx', 'abr.gmy'] self.axis = ['abr.omega', 'abr.gmx', 'abr.gmy']
# Set parameters from meaningful inputs # Set parameters from meaningful inputs
self.abr_command = AbrCmd.RASTER_SCAN self.abr_command = AbrCmd.RASTER_SCAN
self.abr_reset = True
self.scan_positions = parameter['kwargs'].get("positions") self.scan_positions = parameter['kwargs'].get("positions")
self.scan_stepnum_x = parameter['kwargs'].get("steps_x") self.scan_stepnum_x = parameter['kwargs'].get("steps_x")
self.scan_stepnum_y = parameter['kwargs'].get("steps_y") self.scan_stepnum_y = parameter['kwargs'].get("steps_y")
@@ -913,7 +923,6 @@ class MeasureGridStill(AerotechFlyscanBase):
self.axis = ['abr.gmx', 'abr.gmy', 'abr.omega'] self.axis = ['abr.gmx', 'abr.gmy', 'abr.omega']
# Set parameters from meaningful inputs # Set parameters from meaningful inputs
self.abr_command = AbrCmd.RASTER_SCAN_STILL self.abr_command = AbrCmd.RASTER_SCAN_STILL
self.abr_reset = True
self.scan_positions = parameter['kwargs'].get("positions") self.scan_positions = parameter['kwargs'].get("positions")
self.scan_stepnum_x = parameter['kwargs'].get("steps_x") self.scan_stepnum_x = parameter['kwargs'].get("steps_x")
self.scan_stepnum_y = parameter['kwargs'].get("steps_y") self.scan_stepnum_y = parameter['kwargs'].get("steps_y")
@@ -1005,7 +1014,6 @@ class MeasureJungfrau(AerotechFlyscanBase):
self.scan_sleep = parameter['kwargs'].get("sleep", 0.1) self.scan_sleep = parameter['kwargs'].get("sleep", 0.1)
self.abr_timeout = None if self.scan_sleep <= 0 else 5.0 + (self.scan_num_rows * self.scan_sleep) + (self.scan_num_cols * self.scan_num_rows * self.scan_exp_time) self.abr_timeout = None if self.scan_sleep <= 0 else 5.0 + (self.scan_num_rows * self.scan_sleep) + (self.scan_num_cols * self.scan_num_rows * self.scan_exp_time)
self.abr_command = AbrCmd.JUNGFRAU self.abr_command = AbrCmd.JUNGFRAU
self.abr_reset = True
self.abr_complete = True self.abr_complete = True
self.abr_globals = { self.abr_globals = {
"var_1" : self.scan_stepnum_x, "var_1" : self.scan_stepnum_x,