diff --git a/pxiii_bec/device_configs/x06da_device_config.yaml b/pxiii_bec/device_configs/x06da_device_config.yaml index e312153..039b5e7 100644 --- a/pxiii_bec/device_configs/x06da_device_config.yaml +++ b/pxiii_bec/device_configs/x06da_device_config.yaml @@ -345,7 +345,7 @@ omega: abr: description: Aerotech ABR motion system deviceClass: pxiii_bec.devices.AerotechAbrStage - deviceConfig: {prefix: X06DA-ES} + deviceConfig: {prefix: 'X06DA-ES'} onFailure: buffer enabled: true readoutPriority: monitored diff --git a/pxiii_bec/devices/A3200.py b/pxiii_bec/devices/A3200.py index 467d29c..b43f18f 100644 --- a/pxiii_bec/devices/A3200.py +++ b/pxiii_bec/devices/A3200.py @@ -81,7 +81,7 @@ Examples import time 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 @@ -185,6 +185,12 @@ class AerotechAbrStage(Device): _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) + # 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 raster_scan_done = Component(EpicsSignal, "-GRD:SCAN-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() + + 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): self.scan_command.set(CMD_NONE, settle_time=settle_time).wait() self.set_axis_mode("direct", settle_time=settle_time) diff --git a/pxiii_bec/devices/A3200utils.py b/pxiii_bec/devices/A3200utils.py index 57b11f9..4ab5af8 100644 --- a/pxiii_bec/devices/A3200utils.py +++ b/pxiii_bec/devices/A3200utils.py @@ -25,10 +25,29 @@ MEASURING_MODE = 1 class A3200Axis(PVPositioner): - """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 diagnostics. + """ Positioner wrapper for A3200 axes + + + 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'] @@ -74,7 +93,7 @@ class A3200Axis(PVPositioner): """ Patching the parent's PVs into the axis class to check for direct/locked mode""" if parent is None: 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: return suffix return suffix diff --git a/pxiii_bec/scans/__init__.py b/pxiii_bec/scans/__init__.py index 9ca53ef..2b4690a 100644 --- a/pxiii_bec/scans/__init__.py +++ b/pxiii_bec/scans/__init__.py @@ -1 +1 @@ -from .mx_measurements import MeasureFastOmega \ No newline at end of file +from .mx_measurements import MeasureFastOmega, MeasureStandardWedge, MeasureVerticalLine, MeasureScanSlits, MeasureRasterSimple, MeasureScreening, MeasureRepeatedOscillation, MeasureMSOX, MeasureGrid \ No newline at end of file diff --git a/pxiii_bec/scans/mx_measurements.py b/pxiii_bec/scans/mx_measurements.py index a4dcad3..5cd99a7 100644 --- a/pxiii_bec/scans/mx_measurements.py +++ b/pxiii_bec/scans/mx_measurements.py @@ -45,12 +45,19 @@ class AbrCmd: class AerotechFlyscanBase(AsyncFlyScanBase): """ Base class for MX flyscans - Low-level base class for standard scans at the PX beamlines at SLS. Theese scans use the - A3200 rotation stage and the actual experiment is performed using an AeroBasic script - controlled via global variables. + Low-level base class for standard scans at the PX beamlines at SLS. Theese + scans use the A3200 rotation stage and the actual experiment is performed + 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. - + + Parameters: + ----------- + abr_complete : bool + Wait for the launched ABR task to complete. """ scan_report_hint = "table" arg_input = {} @@ -59,7 +66,6 @@ class AerotechFlyscanBase(AsyncFlyScanBase): # Aerotech stage config abr_command = None abr_globals = {} - abr_reset = False abr_raster_reset =False abr_complete = False abr_timeout = None @@ -76,8 +82,6 @@ class AerotechFlyscanBase(AsyncFlyScanBase): self.abr_command = self.caller_kwargs.get("abr_command") if "abr_globals" in self.caller_kwargs: 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: self.abr_raster_reset = self.caller_kwargs.get("abr_raster_reset") if "abr_complete" in self.caller_kwargs: @@ -86,17 +90,19 @@ class AerotechFlyscanBase(AsyncFlyScanBase): self.abr_timeout = self.caller_kwargs.get("abr_timeout") 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") if stat not in (0, "OK"): 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: - 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") + # Reset the raster scan engine if self.abr_raster_reset: 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...") # Wait for grid scanner to finish - if self.abr_complete and self.abr_timeout is not None: - st = yield from self.stubs.send_rpc_and_wait("abr", "complete", self.abr_timeout) - st.wait() + if self.abr_complete: + if self.abr_timeout is not None: + 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): """Set scan progress to 1 to finish the scan""" @@ -175,6 +185,7 @@ class MeasureStandardWedge(AerotechFlyscanBase): "var_3" : self.scan_move_time, "var_4" : self.scan_ready_rate, } + logger.info(self.abr_globals) # Call base class super().__init__(parameter=parameter, **kwargs) @@ -204,7 +215,7 @@ class MeasureVerticalLine(AerotechFlyscanBase): exp_time : float Eeffective exposure time per step [s] """ - scan_name = "measure_vline" + scan_name = "vertical_line" required_kwargs = ["exp_time", "range_y", "steps_y"] def __init__(self, *args, parameter: dict = None, **kwargs): @@ -515,6 +526,7 @@ class MeasureFastOmega(AerotechFlyscanBase): "var_3" : self.scan_move_time, "var_4" : self.scan_acceleration, } + logger.info(self.abr_globals) # Call base class super().__init__(parameter=parameter, **kwargs) @@ -750,7 +762,6 @@ class MeasureMSOX(AerotechFlyscanBase): self.scan_stepnum = parameter['kwargs'].get("steps", 1) self.scan_settle_time = parameter['kwargs'].get("settle_time", 0) self.abr_command = AbrCmd.MSOX - self.abr_reset = True self.abr_globals = { "var_1" : self.scan_start, "var_2" : self.scan_range, @@ -814,7 +825,6 @@ class MeasureGrid(AerotechFlyscanBase): self.axis = ['abr.omega', 'abr.gmx', 'abr.gmy'] # Set parameters from meaningful inputs self.abr_command = AbrCmd.RASTER_SCAN - self.abr_reset = True self.scan_positions = parameter['kwargs'].get("positions") self.scan_stepnum_x = parameter['kwargs'].get("steps_x") self.scan_stepnum_y = parameter['kwargs'].get("steps_y") @@ -913,7 +923,6 @@ class MeasureGridStill(AerotechFlyscanBase): self.axis = ['abr.gmx', 'abr.gmy', 'abr.omega'] # Set parameters from meaningful inputs self.abr_command = AbrCmd.RASTER_SCAN_STILL - self.abr_reset = True self.scan_positions = parameter['kwargs'].get("positions") self.scan_stepnum_x = parameter['kwargs'].get("steps_x") 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.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_reset = True self.abr_complete = True self.abr_globals = { "var_1" : self.scan_stepnum_x,