Aerotech scans work nicely
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 @@
|
|||||||
from .mx_measurements import MeasureFastOmega
|
from .mx_measurements import MeasureFastOmega, MeasureStandardWedge, MeasureVerticalLine, MeasureScanSlits, MeasureRasterSimple, MeasureScreening, MeasureRepeatedOscillation, MeasureMSOX, MeasureGrid
|
||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user