refactor: fixed formatter for aerotech

This commit is contained in:
wakonig_k 2024-03-12 16:30:13 +01:00
parent 75360a9636
commit 573da8a20b
2 changed files with 161 additions and 145 deletions

View File

@ -40,5 +40,12 @@ from .aerotech.AerotechAutomation1 import (
) )
from .SpmBase import SpmBase from .SpmBase import SpmBase
from .aerotech.AerotechAutomation1 import aa1Controller, aa1Tasks, aa1GlobalVariables, aa1GlobalVariableBindings, aa1AxisPsoDistance, aa1AxisDriveDataCollection, EpicsMotorX from .aerotech.AerotechAutomation1 import (
aa1Controller,
aa1Tasks,
aa1GlobalVariables,
aa1GlobalVariableBindings,
aa1AxisPsoDistance,
aa1AxisDriveDataCollection,
EpicsMotorX,
)

View File

@ -38,8 +38,7 @@ from collections import OrderedDict
class EpicsMotorX(EpicsMotor): class EpicsMotorX(EpicsMotor):
""" Special motor class that provides flyer interface and progress bar. """Special motor class that provides flyer interface and progress bar."""
"""
SUB_PROGRESS = "progress" SUB_PROGRESS = "progress"
@ -88,7 +87,10 @@ class EpicsMotorX(EpicsMotor):
"""Progress update on the scan""" """Progress update on the scan"""
if (self._startPosition is None) or (self._targetPosition is None) or (not self.moving): if (self._startPosition is None) or (self._targetPosition is None) or (not self.moving):
self._run_subs( self._run_subs(
sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1, sub_type=self.SUB_PROGRESS,
value=1,
max_value=1,
done=1,
) )
return return
@ -105,8 +107,7 @@ class EpicsMotorX(EpicsMotor):
class EpicsPassiveRO(EpicsSignalRO): class EpicsPassiveRO(EpicsSignalRO):
""" Small helper class to read PVs that need to be processed first. """Small helper class to read PVs that need to be processed first."""
"""
def __init__(self, read_pv, *, string=False, name=None, **kwargs): def __init__(self, read_pv, *, string=False, name=None, **kwargs):
super().__init__(read_pv, string=string, name=name, **kwargs) super().__init__(read_pv, string=string, name=name, **kwargs)
@ -159,7 +160,7 @@ class aa1Controller(Device):
class aa1Tasks(Device): class aa1Tasks(Device):
""" Task management API """Task management API
The place to manage tasks and AeroScript user files on the controller. The place to manage tasks and AeroScript user files on the controller.
You can read/write/compile/execute AeroScript files and also retrieve You can read/write/compile/execute AeroScript files and also retrieve
@ -217,7 +218,7 @@ class aa1Tasks(Device):
parent=None, parent=None,
**kwargs, **kwargs,
): ):
""" __init__ MUST have a full argument list""" """__init__ MUST have a full argument list"""
super().__init__( super().__init__(
prefix=prefix, prefix=prefix,
name=name, name=name,
@ -237,7 +238,10 @@ class aa1Tasks(Device):
"""Progress update on the scan""" """Progress update on the scan"""
value = self.progress() value = self.progress()
self._run_subs( self._run_subs(
sub_type=self.SUB_PROGRESS, value=value, max_value=1, done=1, sub_type=self.SUB_PROGRESS,
value=value,
max_value=1,
done=1,
) )
def _progress(self) -> None: def _progress(self) -> None:
@ -251,7 +255,7 @@ class aa1Tasks(Device):
return 1 return 1
def readFile(self, filename: str) -> str: def readFile(self, filename: str) -> str:
""" Read a file from the controller """ """Read a file from the controller"""
# Have to use CHAR array due to EPICS LSI bug... # Have to use CHAR array due to EPICS LSI bug...
self.fileName.set(filename).wait() self.fileName.set(filename).wait()
filebytes = self._fileRead.get() filebytes = self._fileRead.get()
@ -262,12 +266,12 @@ class aa1Tasks(Device):
return filetext return filetext
def writeFile(self, filename: str, filetext: str) -> None: def writeFile(self, filename: str, filetext: str) -> None:
""" Write a file to the controller """ """Write a file to the controller"""
self.fileName.set(filename).wait() self.fileName.set(filename).wait()
self._fileWrite.set(filetext).wait() self._fileWrite.set(filetext).wait()
def runScript(self, filename: str, taskIndex: int == 2, filetext=None, settle_time=0.5) -> None: def runScript(self, filename: str, taskIndex: int == 2, filetext=None, settle_time=0.5) -> None:
""" Run a script file that either exists, or is newly created and compiled""" """Run a script file that either exists, or is newly created and compiled"""
self.configure({"text": filetext, "filename": filename, "taskIndex": taskIndex}) self.configure({"text": filetext, "filename": filename, "taskIndex": taskIndex})
print("Runscript configured") print("Runscript configured")
@ -275,7 +279,7 @@ class aa1Tasks(Device):
print("Runscript waited") print("Runscript waited")
def execute(self, text: str, taskIndex: int = 3, mode: str = 0, settle_time=0.5): def execute(self, text: str, taskIndex: int = 3, mode: str = 0, settle_time=0.5):
""" Run a short text command on the Automation1 controller""" """Run a short text command on the Automation1 controller"""
print(f"Executing program on task: {taskIndex}") print(f"Executing program on task: {taskIndex}")
self.configure({"text": text, "taskIndex": taskIndex, "mode": mode}) self.configure({"text": text, "taskIndex": taskIndex, "mode": mode})
@ -288,8 +292,7 @@ class aa1Tasks(Device):
return raw return raw
def configure(self, d: dict = {}) -> tuple: def configure(self, d: dict = {}) -> tuple:
""" Configuration interface for flying """Configuration interface for flying"""
"""
# Unrolling the configuration dict # Unrolling the configuration dict
text = str(d["text"]) if "text" in d else None text = str(d["text"]) if "text" in d else None
filename = str(d["filename"]) if "filename" in d else None filename = str(d["filename"]) if "filename" in d else None
@ -342,15 +345,15 @@ class aa1Tasks(Device):
########################################################################## ##########################################################################
# Bluesky stepper interface # Bluesky stepper interface
def stage(self) -> None: def stage(self) -> None:
""" Default staging """ """Default staging"""
super().stage() super().stage()
def unstage(self) -> None: def unstage(self) -> None:
""" Default unstaging """ """Default unstaging"""
super().unstage() super().unstage()
def trigger(self, settle_time=0.2) -> Status: def trigger(self, settle_time=0.2) -> Status:
""" Execute the script on the configured task""" """Execute the script on the configured task"""
if self._isStepConfig: if self._isStepConfig:
return self.kickoff(settle_time) return self.kickoff(settle_time)
else: else:
@ -361,13 +364,13 @@ class aa1Tasks(Device):
return status return status
def stop(self): def stop(self):
""" Stop the currently selected task """ """Stop the currently selected task"""
self.switch.set("Stop").wait() self.switch.set("Stop").wait()
########################################################################## ##########################################################################
# Flyer interface # Flyer interface
def kickoff(self, settle_time=0.2) -> DeviceStatus: def kickoff(self, settle_time=0.2) -> DeviceStatus:
""" Execute the script on the configured task""" """Execute the script on the configured task"""
if self._isConfigured: if self._isConfigured:
if self._textToExecute is not None: if self._textToExecute is not None:
print(f"Kickoff directly executing string: {self._textToExecute}") print(f"Kickoff directly executing string: {self._textToExecute}")
@ -382,7 +385,7 @@ class aa1Tasks(Device):
return status return status
def complete(self) -> DeviceStatus: def complete(self) -> DeviceStatus:
""" Execute the script on the configured task""" """Execute the script on the configured task"""
print("Called aa1Task.complete()") print("Called aa1Task.complete()")
timestamp_ = 0 timestamp_ = 0
taskIdx = int(self.taskIndex.get()) taskIdx = int(self.taskIndex.get())
@ -418,7 +421,7 @@ class aa1Tasks(Device):
class aa1TaskState(Device): class aa1TaskState(Device):
""" Task state monitoring API """Task state monitoring API
This is the task state monitoring interface for Automation1 tasks. It This is the task state monitoring interface for Automation1 tasks. It
does not launch execution, but can wait for the execution to complete. does not launch execution, but can wait for the execution to complete.
@ -430,7 +433,7 @@ class aa1TaskState(Device):
warnCode = Component(EpicsSignalRO, "WARNING", auto_monitor=True, kind=Kind.hinted) warnCode = Component(EpicsSignalRO, "WARNING", auto_monitor=True, kind=Kind.hinted)
def complete(self) -> StatusBase: def complete(self) -> StatusBase:
""" Bluesky flyer interface""" """Bluesky flyer interface"""
print("Called aa1TaskState.complete()") print("Called aa1TaskState.complete()")
# 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
@ -471,7 +474,7 @@ class aa1TaskState(Device):
class aa1DataAcquisition(Device): class aa1DataAcquisition(Device):
""" Controller Data Acquisition - DONT USE at Tomcat """Controller Data Acquisition - DONT USE at Tomcat
This class implements the controller data collection feature of the This class implements the controller data collection feature of the
Automation1 controller. This feature logs various inputs at a Automation1 controller. This feature logs various inputs at a
@ -512,7 +515,7 @@ class aa1DataAcquisition(Device):
_srcAdd = Component(EpicsSignal, "SRC_ADD", kind=Kind.omitted, put_complete=True) _srcAdd = Component(EpicsSignal, "SRC_ADD", kind=Kind.omitted, put_complete=True)
def addAxisSignal(self, axis: int, code: int) -> None: def addAxisSignal(self, axis: int, code: int) -> None:
""" Add a new axis-specific data signal to the DAQ configuration. The """Add a new axis-specific data signal to the DAQ configuration. The
most common signals are PositionFeedback and PositionError. most common signals are PositionFeedback and PositionError.
""" """
self.srcAxis.set(axis).wait() self.srcAxis.set(axis).wait()
@ -520,14 +523,14 @@ class aa1DataAcquisition(Device):
self._srcAdd.set("AXIS").wait() self._srcAdd.set("AXIS").wait()
def addTaskSignal(self, task: int, code: int) -> None: def addTaskSignal(self, task: int, code: int) -> None:
""" Add a new task-specific data signal to the DAQ configuration""" """Add a new task-specific data signal to the DAQ configuration"""
self.srcTask.set(task).wait() self.srcTask.set(task).wait()
self.srcCode.set(code).wait() self.srcCode.set(code).wait()
self._srcAdd.set("TASK").wait() self._srcAdd.set("TASK").wait()
def addSystemSignal(self, code: int) -> None: def addSystemSignal(self, code: int) -> None:
""" Add a new system data signal to the DAQ configuration. The most """Add a new system data signal to the DAQ configuration. The most
common signal is SampleCollectionTime. """ common signal is SampleCollectionTime."""
self.srcCode.set(code).wait() self.srcCode.set(code).wait()
self._srcAdd.set("SYSTEM").wait() self._srcAdd.set("SYSTEM").wait()
@ -536,16 +539,16 @@ class aa1DataAcquisition(Device):
_switch = Component(EpicsSignal, "SET", kind=Kind.omitted, put_complete=True) _switch = Component(EpicsSignal, "SET", kind=Kind.omitted, put_complete=True)
def start(self, mode=DataCollectionMode.Snapshot) -> None: def start(self, mode=DataCollectionMode.Snapshot) -> None:
""" Start a new data collection """ """Start a new data collection"""
self._mode.set(mode).wait() self._mode.set(mode).wait()
self._switch.set("START").wait() self._switch.set("START").wait()
def stop(self) -> None: def stop(self) -> None:
""" Stop a running data collection """ """Stop a running data collection"""
self._switch.set("STOP").wait() self._switch.set("STOP").wait()
def run(self, mode=DataCollectionMode.Snapshot) -> None: def run(self, mode=DataCollectionMode.Snapshot) -> None:
""" Start a new data collection """ """Start a new data collection"""
self._mode.set(mode).wait() self._mode.set(mode).wait()
self._switch.set("START").wait() self._switch.set("START").wait()
# Wait for finishing acquisition # Wait for finishing acquisition
@ -589,7 +592,7 @@ class aa1DataAcquisition(Device):
class aa1GlobalVariables(Device): class aa1GlobalVariables(Device):
""" Global variables """Global variables
This class provides an interface to directly read/write global variables This class provides an interface to directly read/write global variables
on the Automation1 controller. These variables are accesible from script on the Automation1 controller. These variables are accesible from script
@ -631,7 +634,7 @@ class aa1GlobalVariables(Device):
string_rb = Component(EpicsPassiveRO, "STRING-RBV", string=True, kind=Kind.omitted) string_rb = Component(EpicsPassiveRO, "STRING-RBV", string=True, kind=Kind.omitted)
def readInt(self, address: int, size: int = None) -> int: def readInt(self, address: int, size: int = None) -> int:
""" Read a 64-bit integer global variable """ """Read a 64-bit integer global variable"""
if address > self.num_int.get(): if address > self.num_int.get():
raise RuntimeError("Integer address {address} is out of range") raise RuntimeError("Integer address {address} is out of range")
@ -644,7 +647,7 @@ class aa1GlobalVariables(Device):
return self.integerarr_rb.get() return self.integerarr_rb.get()
def writeInt(self, address: int, value) -> None: def writeInt(self, address: int, value) -> None:
""" Write a 64-bit integer global variable """ """Write a 64-bit integer global variable"""
if address > self.num_int.get(): if address > self.num_int.get():
raise RuntimeError("Integer address {address} is out of range") raise RuntimeError("Integer address {address} is out of range")
@ -662,7 +665,7 @@ class aa1GlobalVariables(Device):
raise RuntimeError("Unsupported integer value type: {type(value)}") raise RuntimeError("Unsupported integer value type: {type(value)}")
def readFloat(self, address: int, size: int = None) -> float: def readFloat(self, address: int, size: int = None) -> float:
""" Read a 64-bit double global variable """ """Read a 64-bit double global variable"""
if address > self.num_real.get(): if address > self.num_real.get():
raise RuntimeError("Floating point address {address} is out of range") raise RuntimeError("Floating point address {address} is out of range")
@ -675,7 +678,7 @@ class aa1GlobalVariables(Device):
return self.realarr_rb.get() return self.realarr_rb.get()
def writeFloat(self, address: int, value) -> None: def writeFloat(self, address: int, value) -> None:
""" Write a 64-bit float global variable """ """Write a 64-bit float global variable"""
if address > self.num_real.get(): if address > self.num_real.get():
raise RuntimeError("Float address {address} is out of range") raise RuntimeError("Float address {address} is out of range")
@ -693,7 +696,7 @@ class aa1GlobalVariables(Device):
raise RuntimeError("Unsupported float value type: {type(value)}") raise RuntimeError("Unsupported float value type: {type(value)}")
def readString(self, address: int) -> str: def readString(self, address: int) -> str:
""" Read a 40 letter string global variable """Read a 40 letter string global variable
ToDo: Automation 1 strings are 256 bytes ToDo: Automation 1 strings are 256 bytes
""" """
if address > self.num_string.get(): if address > self.num_string.get():
@ -703,7 +706,7 @@ class aa1GlobalVariables(Device):
return self.string_rb.get() return self.string_rb.get()
def writeString(self, address: int, value) -> None: def writeString(self, address: int, value) -> None:
""" Write a 40 bytes string global variable """ """Write a 40 bytes string global variable"""
if address > self.num_string.get(): if address > self.num_string.get():
raise RuntimeError("Integer address {address} is out of range") raise RuntimeError("Integer address {address} is out of range")
@ -715,7 +718,7 @@ class aa1GlobalVariables(Device):
class aa1GlobalVariableBindings(Device): class aa1GlobalVariableBindings(Device):
""" Polled global variables """Polled global variables
This class provides an interface to read/write the first few global variables This class provides an interface to read/write the first few global variables
on the Automation1 controller. These variables are continuously polled on the Automation1 controller. These variables are continuously polled
@ -842,7 +845,7 @@ class aa1GlobalVariableBindings(Device):
class aa1AxisIo(Device): class aa1AxisIo(Device):
""" Analog / digital Input-Output """Analog / digital Input-Output
This class provides convenience wrappers around the Aerotech API's axis This class provides convenience wrappers around the Aerotech API's axis
specific IO functionality. Note that this is a low-speed API, actual work specific IO functionality. Note that this is a low-speed API, actual work
@ -888,7 +891,7 @@ class aa1AxisIo(Device):
class aa1AxisPsoBase(Device): class aa1AxisPsoBase(Device):
""" Position Sensitive Output - Base class """Position Sensitive Output - Base class
This class provides convenience wrappers around the Aerotech IOC's PSO This class provides convenience wrappers around the Aerotech IOC's PSO
functionality. As a base class, it's just a collection of PVs without functionality. As a base class, it's just a collection of PVs without
@ -954,7 +957,7 @@ class aa1AxisPsoBase(Device):
outSource = Component(EpicsSignal, "SOURCE", put_complete=True, kind=Kind.omitted) outSource = Component(EpicsSignal, "SOURCE", put_complete=True, kind=Kind.omitted)
def fire(self, settle_time=None): def fire(self, settle_time=None):
""" Fire a single PSO event (i.e. manual software trigger)""" """Fire a single PSO event (i.e. manual software trigger)"""
self._eventSingle.set(1, settle_time=settle_time).wait() self._eventSingle.set(1, settle_time=settle_time).wait()
def toggle(self): def toggle(self):
@ -965,7 +968,7 @@ class aa1AxisPsoBase(Device):
class aa1AxisPsoDistance(aa1AxisPsoBase): class aa1AxisPsoDistance(aa1AxisPsoBase):
""" Position Sensitive Output - Distance mode """Position Sensitive Output - Distance mode
This class provides convenience wrappers around the Aerotech API's PSO This class provides convenience wrappers around the Aerotech API's PSO
functionality in distance mode. It uses event-waveform concept to produce functionality in distance mode. It uses event-waveform concept to produce
@ -1010,7 +1013,7 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
parent=None, parent=None,
**kwargs, **kwargs,
): ):
""" __init__ MUST have a full argument list""" """__init__ MUST have a full argument list"""
super().__init__( super().__init__(
prefix=prefix, prefix=prefix,
name=name, name=name,
@ -1028,7 +1031,10 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
if self.dstArrayDepleted.value: if self.dstArrayDepleted.value:
print("PSO array depleted") print("PSO array depleted")
self._run_subs( self._run_subs(
sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1, sub_type=self.SUB_PROGRESS,
value=1,
max_value=1,
done=1,
) )
return return
@ -1045,7 +1051,7 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
# ######################################################################## # ########################################################################
# PSO high level interface # PSO high level interface
def configure(self, d: dict = {}) -> tuple: def configure(self, d: dict = {}) -> tuple:
""" Simplified configuration interface to access the most common """Simplified configuration interface to access the most common
functionality for distance mode PSO. functionality for distance mode PSO.
:param distance: The trigger distance or the array of distances between subsequent points. :param distance: The trigger distance or the array of distances between subsequent points.
@ -1111,7 +1117,7 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
# ######################################################################## # ########################################################################
# Bluesky step scan interface # Bluesky step scan interface
def complete(self, settle_time=0.1) -> DeviceStatus: def complete(self, settle_time=0.1) -> DeviceStatus:
""" DDC just reads back whatever is available in the buffers""" """DDC just reads back whatever is available in the buffers"""
sleep(settle_time) sleep(settle_time)
status = DeviceStatus(self) status = DeviceStatus(self)
status.set_finished() status.set_finished()
@ -1143,7 +1149,7 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
return status return status
def complete(self) -> DeviceStatus: def complete(self) -> DeviceStatus:
""" Bluesky flyer interface""" """Bluesky flyer interface"""
# Array mode waits until the buffer is empty # Array mode waits until the buffer is empty
if hasattr(self, "_distanceValue") and isinstance( if hasattr(self, "_distanceValue") and isinstance(
self._distanceValue, (np.ndarray, list, tuple) self._distanceValue, (np.ndarray, list, tuple)
@ -1189,7 +1195,7 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
class aa1AxisPsoWindow(aa1AxisPsoBase): class aa1AxisPsoWindow(aa1AxisPsoBase):
""" Position Sensitive Output - Window mode """Position Sensitive Output - Window mode
This class provides convenience wrappers around the Aerotech API's PSO This class provides convenience wrappers around the Aerotech API's PSO
functionality in window mode. It can either use the event-waveform concept functionality in window mode. It can either use the event-waveform concept
@ -1219,7 +1225,7 @@ class aa1AxisPsoWindow(aa1AxisPsoBase):
parent=None, parent=None,
**kwargs, **kwargs,
): ):
""" __init__ MUST have a full argument list""" """__init__ MUST have a full argument list"""
super().__init__( super().__init__(
prefix=prefix, prefix=prefix,
name=name, name=name,
@ -1235,7 +1241,7 @@ class aa1AxisPsoWindow(aa1AxisPsoBase):
# ######################################################################## # ########################################################################
# PSO high level interface # PSO high level interface
def configure(self, d: dict = {}) -> tuple: def configure(self, d: dict = {}) -> tuple:
""" Simplified configuration interface to access the most common """Simplified configuration interface to access the most common
functionality for distance mode PSO. functionality for distance mode PSO.
:param distance: The trigger distance or the array of distances between subsequent points. :param distance: The trigger distance or the array of distances between subsequent points.
@ -1328,7 +1334,7 @@ class aa1AxisPsoWindow(aa1AxisPsoBase):
class aa1AxisDriveDataCollection(Device): class aa1AxisDriveDataCollection(Device):
""" Axis data collection """Axis data collection
This class provides convenience wrappers around the Aerotech API's axis This class provides convenience wrappers around the Aerotech API's axis
specific data collection functionality. This module allows to record specific data collection functionality. This module allows to record
@ -1393,7 +1399,10 @@ class aa1AxisDriveDataCollection(Device):
"""Progress update on the scan""" """Progress update on the scan"""
if self.state.value not in (2, "Acquiring"): if self.state.value not in (2, "Acquiring"):
self._run_subs( self._run_subs(
sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1, sub_type=self.SUB_PROGRESS,
value=1,
max_value=1,
done=1,
) )
return return
@ -1441,14 +1450,14 @@ class aa1AxisDriveDataCollection(Device):
return status return status
def complete(self, settle_time=0.1) -> DeviceStatus: def complete(self, settle_time=0.1) -> DeviceStatus:
""" DDC just reads back whatever is available in the buffers""" """DDC just reads back whatever is available in the buffers"""
sleep(settle_time) sleep(settle_time)
status = DeviceStatus(self) status = DeviceStatus(self)
status.set_finished() status.set_finished()
return status return status
def _collect(self, index=0): def _collect(self, index=0):
""" Force a readback of the data buffer """Force a readback of the data buffer
Note that there's a weird behaviour in ophyd that it issues an Note that there's a weird behaviour in ophyd that it issues an
initial update event with the initial value but 0 timestamp. Theese initial update event with the initial value but 0 timestamp. Theese