A3200 cleanup

This commit is contained in:
Unknown MX Person
2024-12-13 17:44:52 +01:00
parent 78c75b1769
commit add46d8b0d
4 changed files with 202 additions and 126 deletions
+32 -112
View File
@@ -80,56 +80,20 @@ Examples
""" """
import time import time
from ophyd import Component, Device, EpicsSignal, EpicsSignalRO, Kind from ophyd import Component, Device, EpicsSignal, EpicsSignalRO, Kind
from ophyd.status import MoveStatus, SubscriptionStatus, DeviceStatus from ophyd.status import SubscriptionStatus
from .A3200utils import A3200Axis, A3200RasterScanner, A3200Oscillator from .A3200utils import A3200RasterScanner, A3200Oscillator
from .A3200enums import *
try:
from bec_lib import bec_logger
logger = bec_logger.logger
except ModuleNotFoundError:
import logging
logger = logging.getLogger("A3200")
ABR_DONE = 0 # pylint: disable=logging-fstring-interpolation
ABR_READY = 1
ABR_BUSY = 2
GRID_SCAN_BUSY = 0
GRID_SCAN_DONE = 1
DIRECT_MODE = 0
MEASURING_MODE = 1
SHUTTER_CLOSE = 0
SHUTTER_OPEN = 1
FULL_PERIOD = 0
HALF_PERIOD = 1
CMD_NONE = 0
CMD_RASTER_SCAN_SIMPLE = 1
CMD_MEASURE_STANDARD = 2
CMD_VERTICAL_LINE_SCAN = 3
CMD_SCREENING = 4
CMD_SUPER_FAST_OMEGA = 5
CMD_STILL_WEDGE = 6
CMD_STILLS = 7
CMD_REPEAT_SINGLE_OSCILLATION = 8
CMD_SINGLE_OSCILLATION = 9
CMD_OLD_FASHIONED = 10
CMD_RASTER_SCAN = 11
CMD_JET_ROTATION = 12
CMD_X_HELICAL = 13
CMD_X_RUNSEQ = 14
CMD_JUNGFRAU = 15
CMD_MSOX = 16
CMD_SLIT_SCAN = 17
CMD_RASTER_SCAN_STILL = 18
CMD_SCAN_SASTT = 19
CMD_SCAN_SASTT_V2 = 20
CMD_SCAN_SASTT_V3 = 21
AXIS_OMEGA = 1
AXIS_GMX = 2
AXIS_GMY = 3
AXIS_GMZ = 4
AXIS_STY = 5
AXIS_STZ = 6
class AerotechAbrStage(Device): class AerotechAbrStage(Device):
@@ -165,11 +129,13 @@ class AerotechAbrStage(Device):
raster = Component(A3200RasterScanner, "", kind=Kind.config) raster = Component(A3200RasterScanner, "", kind=Kind.config)
osc = Component(A3200Oscillator, "", kind=Kind.config) osc = Component(A3200Oscillator, "", kind=Kind.config)
# Positioners for the motor axes # Status flags for all axes
omega = Component(A3200Axis, "-DF1:OMEGA", kind=Kind.hinted) omega_done = Component(EpicsSignalRO, "-DF1:OMEGA-DONE", kind=Kind.normal)
gmx = Component(A3200Axis, "-DF1:GMX", kind=Kind.hinted) gmx_done = Component(EpicsSignalRO, "-DF1:GMX-DONE", kind=Kind.normal)
gmy = Component(A3200Axis, "-DF1:GMY", kind=Kind.hinted) gmy_done = Component(EpicsSignalRO, "-DF1:GMY-DONE", kind=Kind.normal)
gmz = Component(A3200Axis, "-DF1:GMZ", kind=Kind.hinted) gmz_done = Component(EpicsSignalRO, "-DF1:GMZ-DONE", kind=Kind.normal)
scan_command = Component( scan_command = Component(
EpicsSignal, "-PSO:CMD", put_complete=True, kind=Kind.omitted) EpicsSignal, "-PSO:CMD", put_complete=True, kind=Kind.omitted)
@@ -270,19 +236,13 @@ class AerotechAbrStage(Device):
new = self.read_configuration() new = self.read_configuration()
return old, new return old, new
def complete(self, timeout=None) -> DeviceStatus:
""" ToDo: WTF was this device doing here? Whatever...
"""
return self.raster.complete()
def complete(self, timeout=None) -> SubscriptionStatus: def complete(self, timeout=None) -> SubscriptionStatus:
""" Waits for task execution """ Waits for task execution
NOTE: Original complete was raster scanner complete... NOTE: Original complete was raster scanner complete...
""" """
# Define wait until the busy flag goes down (excluding initial update) # Define wait until the busy flag goes down (excluding initial update)
def is_idle(*args, old_value, value, timestamp, **kwargs): def is_idle(*args, value, _, **kwargs):
return bool(value==1) return bool(value==1)
# Subscribe and wait for update # Subscribe and wait for update
@@ -290,46 +250,14 @@ class AerotechAbrStage(Device):
return status return status
def reset(self, settle_time=0.1): def reset(self, settle_time=0.1):
"""Attempts to reset the currently running measurement task on the A3200"""
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)
def is_ioc_ok(self): def is_ioc_ok(self):
"""Checks execution status"""
return 0 == self.status.get() return 0 == self.status.get()
def is_ready_and_willing(self):
return self.is_ioc_ok() and self.omega.is_homed()
def is_omega_ok(self):
return 0 == self.self.omega.status.get()
def is_homed(self):
"""Are the motors homed?"""
return 1 == self.omega.is_homed.get()
def do_homing(self, wait=True):
"""Execute the homing procedure.
Executes the homing procedure and waits (default) until it is completed.
TODO: Return a status object to do this wwith futures and monitoring.
PARAMETERS
`wait` true / false if the routine is to wait for the homing to finish.
"""
self.omega.home.set(1, settle_time=1).wait()
if not wait:
return
while not self.omega.is_homed():
time.sleep(0.2)
@property
def velocity(self):
return self.omega.velocity.get()
@velocity.setter
def velocity(self, value):
self.omega.velocity.set(value).wait()
@property @property
def exp_time(self): def exp_time(self):
return self.osc.exp_time.get() return self.osc.exp_time.get()
@@ -396,12 +324,11 @@ class AerotechAbrStage(Device):
self.osc.get_ready.set(1, settle_time=0.1).wait() self.osc.get_ready.set(1, settle_time=0.1).wait()
if wait: if wait:
for n in range(10): for _ in range(10):
try: try:
self.wait_status(ABR_READY, timeout=5).wait() self.wait_status(ABR_READY, timeout=5).wait()
except RuntimeWarning as e: except RuntimeWarning as ex:
print(str(e), end=" ") logger.warning(f"{ex} --- trying ready again.")
print(" --- trying ready again.")
self.osc.get_ready.set(1).wait() self.osc.get_ready.set(1).wait()
@@ -418,8 +345,8 @@ class AerotechAbrStage(Device):
time.sleep(0.1) time.sleep(0.1)
try: try:
moving = self.is_moving() moving = self.is_moving()
except: except Exception as ex:
print("Aerotech() Failed to retrieve moving state for axes omega, gmx, gmy, gmz") logger.error(f"Failed to retrieve moving state for axes: {ex}")
moving = True moving = True
timeisup = timeout < time.time() timeisup = timeout < time.time()
@@ -427,9 +354,10 @@ class AerotechAbrStage(Device):
raise RuntimeWarning("timeout waiting for all axis to stop moving") raise RuntimeWarning("timeout waiting for all axis to stop moving")
def is_moving(self): def is_moving(self):
return ( """Chechs if all axes are DONE"""
self.omega.moving return not (
and self.gmx.moving and self.gmy.moving and self.gmz.moving ) self.omega_done.get()
and self.gmx_done.get() and self.gmy_done.get() and self.gmz_done.get() )
def stop(self, wait_after_reload=1.0): def stop(self, wait_after_reload=1.0):
"""Stops current motions; reloads aerobasic programs; goes DIRECT """Stops current motions; reloads aerobasic programs; goes DIRECT
@@ -449,21 +377,13 @@ class AerotechAbrStage(Device):
"""Starts the previously configured exposure.""" """Starts the previously configured exposure."""
self.wait_for_movements() self.wait_for_movements()
self.osc.taskStart.set(1).wait() self.osc.taskStart.set(1).wait()
for n in range(10): for _ in range(10):
try: try:
self.osc.wait_status(ABR_BUSY, timeout=1) self.osc.wait_status(ABR_BUSY, timeout=1)
except RuntimeWarning as e: except RuntimeWarning as ex:
print("%s --- trying start again.", str(e)) logger.error(f"{ex} --- trying start again.")
self.osc.kickoff() self.osc.kickoff()
def move(self, position, wait=False, velocity=None, relative=None, direct=False,
**kwargs) -> MoveStatus:
""" Move the omega axis
"""
return self.omega.move(position, wait=wait, velocity=velocity, relative=relative,
direct=direct, **kwargs)
if __name__ == "__main__": if __name__ == "__main__":
abr = AerotechAbrStage(prefix="X06DA-ES", name="abr") abr = AerotechAbrStage(prefix="X06DA-ES", name="abr")
+113
View File
@@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
"""
Enumerations for the MX specific Aerotech A3200 stage.
@author: mohacsi_i
"""
# pylint: disable=too-few-public-methods
class AbrStatus:
"""ABR measurement task status"""
DONE = 0
READY = 1
BUSY = 2
ABR_DONE = 0
ABR_READY = 1
ABR_BUSY = 2
class AbrGridStatus:
"""ABR grid scan status"""
BUSY = 0
DONE = 1
GRID_SCAN_BUSY = 0
GRID_SCAN_DONE = 1
class AbrMode:
"""ABR mode status"""
DIRECT = 0
MEASURING = 1
DIRECT_MODE = 0
MEASURING_MODE = 1
class AbrShutterStatus:
"""ABR shutter status"""
CLOSE = 0
OPEN = 1
SHUTTER_CLOSE = 0
SHUTTER_OPEN = 1
class AbrGridPeriod:
"""ABR grid period"""
FULL = 0
HALF = 1
FULL_PERIOD = 0
HALF_PERIOD = 1
class AbrCmd:
"""ABR command table"""
NONE = 0
RASTER_SCAN_SIMPLE = 1
MEASURE_STANDARD = 2
VERTICAL_LINE_SCAN = 3
SCREENING = 4
SUPER_FAST_OMEGA = 5
STILL_WEDGE = 6
STILLS = 7
REPEAT_SINGLE_OSCILLATION = 8
SINGLE_OSCILLATION = 9
OLD_FASHIONED = 10
RASTER_SCAN = 11
JET_ROTATION = 12
X_HELICAL = 13
X_RUNSEQ = 14
JUNGFRAU = 15
MSOX = 16
SLIT_SCAN = 17
RASTER_SCAN_STILL = 18
SCAN_SASTT = 19
SCAN_SASTT_V2 = 20
SCAN_SASTT_V3 = 21
CMD_NONE = 0
CMD_RASTER_SCAN_SIMPLE = 1
CMD_MEASURE_STANDARD = 2
CMD_VERTICAL_LINE_SCAN = 3
CMD_SCREENING = 4
CMD_SUPER_FAST_OMEGA = 5
CMD_STILL_WEDGE = 6
CMD_STILLS = 7
CMD_REPEAT_SINGLE_OSCILLATION = 8
CMD_SINGLE_OSCILLATION = 9
CMD_OLD_FASHIONED = 10
CMD_RASTER_SCAN = 11
CMD_JET_ROTATION = 12
CMD_X_HELICAL = 13
CMD_X_RUNSEQ = 14
CMD_JUNGFRAU = 15
CMD_MSOX = 16
CMD_SLIT_SCAN = 17
CMD_RASTER_SCAN_STILL = 18
CMD_SCAN_SASTT = 19
CMD_SCAN_SASTT_V2 = 20
CMD_SCAN_SASTT_V3 = 21
class AbrAxis:
"""ABR axis index"""
OMEGA = 1
GMX = 2
GMY = 3
GMZ = 4
STY = 5
STZ = 6
AXIS_OMEGA = 1
AXIS_GMX = 2
AXIS_GMY = 3
AXIS_GMZ = 4
AXIS_STY = 5
AXIS_STZ = 6
+51 -14
View File
@@ -5,6 +5,7 @@ Created on Tue Jun 11 11:28:38 2024
@author: mohacsi_i @author: mohacsi_i
""" """
import types import types
import math
from ophyd import (Component, PVPositioner, Device, Signal, EpicsSignal, EpicsSignalRO, Kind, from ophyd import (Component, PVPositioner, Device, Signal, EpicsSignal, EpicsSignalRO, Kind,
PositionerBase) PositionerBase)
from ophyd.status import Status, MoveStatus, SubscriptionStatus, DeviceStatus from ophyd.status import Status, MoveStatus, SubscriptionStatus, DeviceStatus
@@ -14,6 +15,11 @@ from bec_lib import bec_logger
logger = bec_logger.logger logger = bec_logger.logger
from .A3200enums import *
ABR_DONE = 0 ABR_DONE = 0
ABR_READY = 1 ABR_READY = 1
ABR_BUSY = 2 ABR_BUSY = 2
@@ -118,8 +124,8 @@ class A3200Axis(PVPositioner):
self.vmin.set(vmin).wait() self.vmin.set(vmin).wait()
self.vmax.set(vmax).wait() self.vmax.set(vmax).wait()
def move(self, position, velocity=None, wait=True, relative=False, direct=False, timeout=None, def omove(self, position, wait=True, timeout=None, moved_cb=None, velocity=None, relative=False,
**kwargs) -> MoveStatus: direct=False, **kwargs) -> MoveStatus:
""" Native absolute/relative movement on the A3200 """ """ Native absolute/relative movement on the A3200 """
# Check if we're in direct movement mode # Check if we're in direct movement mode
@@ -164,10 +170,9 @@ class A3200Axis(PVPositioner):
return status return status
def omove(self, position, velocity=None, wait=True, relative=False, direct=False, def move(self, position, wait=True, timeout=None, moved_cb=None, **kwargs) -> MoveStatus:
**kwargs) -> MoveStatus:
""" Exposes the ophyd move command through BEC abstraction""" """ Exposes the ophyd move command through BEC abstraction"""
return self.move(position, velocity, wait, relative, direct, **kwargs) return self.omove(position, wait=wait, timeout=timeout, moved_cb=moved_cb, **kwargs)
def rock(self, distance, counts: int, velocity=None, wait=True) -> Status: def rock(self, distance, counts: int, velocity=None, wait=True) -> Status:
""" Repeated single axis zigzag scan I guess PSO should be configured for this""" """ Repeated single axis zigzag scan I guess PSO should be configured for this"""
@@ -186,6 +191,38 @@ class A3200Axis(PVPositioner):
return status return status
# def is_omega_ok(self):
# """Checks omega axis status"""
# return 0 == self.self.omega.status.get()
# def is_homed(self):
# """Checks if omega is homed"""
# return 1 == self.omega.is_homed.get()
# def do_homing(self, wait=True):
# """Execute the homing procedure.
# Executes the homing procedure on omega and waits (default) until it is completed.
# TODO: Return a status object to do this wwith futures and monitoring.
# PARAMETERS
# `wait` true / false if the routine is to wait for the homing to finish.
# """
# self.omega.home.set(1, settle_time=1).wait()
# if not wait:
# return
# while not self.omega.is_homed():
# time.sleep(0.2)
@@ -305,7 +342,7 @@ class A3200RasterScanner(Device):
try: try:
# Define wait until the busy flag goes down (excluding initial update) # Define wait until the busy flag goes down (excluding initial update)
timestamp_ = 0 timestamp_ = 0
def isReady(*args, old_value, value, timestamp, **kwargs): def is_ready(*args, old_value, value, timestamp, **kwargs):
nonlocal timestamp_ nonlocal timestamp_
result = False if (timestamp_== 0) else bool(value==ABR_READY) result = False if (timestamp_== 0) else bool(value==ABR_READY)
print(f"Old {old_value}\tNew: {value}\tResult: {result}") print(f"Old {old_value}\tNew: {value}\tResult: {result}")
@@ -313,7 +350,7 @@ class A3200RasterScanner(Device):
return result return result
# Subscribe and wait for update # Subscribe and wait for update
status = SubscriptionStatus(self.grid_done, isReady, timeout=2, settle_time=0.5) status = SubscriptionStatus(self.grid_done, is_ready, timeout=2, settle_time=0.5)
status.wait() status.wait()
except TimeoutError as e: except TimeoutError as e:
print(str(e), end=" ") print(str(e), end=" ")
@@ -333,7 +370,7 @@ class A3200RasterScanner(Device):
def wait_scan_done(self, timeout=60.0) -> SubscriptionStatus: def wait_scan_done(self, timeout=60.0) -> SubscriptionStatus:
# Define wait until the busy flag goes down (excluding initial update) # Define wait until the busy flag goes down (excluding initial update)
timestamp_ = 0 timestamp_ = 0
def isReady(*args, old_value, value, timestamp, **kwargs): def is_ready(*args, old_value, value, timestamp, **kwargs):
nonlocal timestamp_ nonlocal timestamp_
result = False if (timestamp_== 0) else bool(value==1) result = False if (timestamp_== 0) else bool(value==1)
print(f"Old {old_value}\tNew: {value}\tResult: {result}") print(f"Old {old_value}\tNew: {value}\tResult: {result}")
@@ -341,7 +378,7 @@ class A3200RasterScanner(Device):
return result return result
# Subscribe and wait for update # Subscribe and wait for update
status = SubscriptionStatus(self.scan_done, isReady, timeout=timeout, settle_time=0.5) status = SubscriptionStatus(self.scan_done, is_ready, timeout=timeout, settle_time=0.5)
status.wait() status.wait()
return status return status
@@ -373,7 +410,7 @@ class A3200RasterScanner(Device):
if timeout is not None: if timeout is not None:
# Define wait until the busy flag goes down (excluding initial update) # Define wait until the busy flag goes down (excluding initial update)
timestamp_ = 0 timestamp_ = 0
def isReady(*args, old_value, value, timestamp, **kwargs): def is_ready(*args, old_value, value, timestamp, **kwargs):
nonlocal timestamp_ nonlocal timestamp_
result = False if (timestamp_== 0) else bool(value==GRID_SCAN_DONE) result = False if (timestamp_== 0) else bool(value==GRID_SCAN_DONE)
print(f"Old {old_value}\tNew: {value}\tResult: {result}") print(f"Old {old_value}\tNew: {value}\tResult: {result}")
@@ -381,7 +418,7 @@ class A3200RasterScanner(Device):
return result return result
# Subscribe and wait for update # Subscribe and wait for update
status = SubscriptionStatus(self.scan_done, isReady, timeout=timeout, settle_time=0.5) status = SubscriptionStatus(self.scan_done, is_ready, timeout=timeout, settle_time=0.5)
return status return status
status = DeviceStatus(self, settle_time=0.5) status = DeviceStatus(self, settle_time=0.5)
status.set_finished() status.set_finished()
@@ -424,13 +461,13 @@ class A3200Oscillator(Device):
SubscriptionStatus : A waitable status subscribed on 'phase' updates. SubscriptionStatus : A waitable status subscribed on 'phase' updates.
""" """
# Define wait until the busy flag goes down (excluding initial update) # Define wait until the busy flag goes down (excluding initial update)
def inStatus(*args, old_value, value, timestamp, **kwargs): def in_status(*args, old_value, value, _, **kwargs):
result = bool(value==target) result = bool(value==target)
print(f"Old {old_value}\tNew: {value}\tResult: {result}") # print(f"Old {old_value}\tNew: {value}\tResult: {result}")
return result return result
# Subscribe and wait for update # Subscribe and wait for update
status = SubscriptionStatus(self.phase, inStatus, timeout=timeout, settle_time=0.5) status = SubscriptionStatus(self.phase, in_status, timeout=timeout, settle_time=0.5)
return status return status
+6
View File
@@ -1,2 +1,8 @@
# -*- coding: utf-8 -*-
"""
Ophyd devices for the PX III beamline, including the MX specific Aerotech A3200 stage.
@author: mohacsi_i
"""
from .A3200 import AerotechAbrStage from .A3200 import AerotechAbrStage
from .A3200utils import A3200Axis from .A3200utils import A3200Axis