This commit is contained in:
mohacsi_i 2024-03-12 10:56:30 +01:00
parent 1b328d31de
commit 1dda0e04ba
2 changed files with 828 additions and 570 deletions

View File

@ -8,38 +8,72 @@ import time
try: try:
from .AerotechAutomation1Enums import * from .AerotechAutomation1Enums import *
from .AerotechAutomation1Enums import (DataCollectionMode, DataCollectionFrequency, from .AerotechAutomation1Enums import (
AxisDataSignal, PsoWindowInput, DriveDataCaptureInput, DriveDataCaptureTrigger, DataCollectionMode,
TaskDataSignal, SystemDataSignal, TomcatSequencerState) DataCollectionFrequency,
AxisDataSignal,
PsoWindowInput,
DriveDataCaptureInput,
DriveDataCaptureTrigger,
TaskDataSignal,
SystemDataSignal,
TomcatSequencerState,
)
except: except:
from AerotechAutomation1Enums import * from AerotechAutomation1Enums import *
from AerotechAutomation1Enums import (DataCollectionMode, DataCollectionFrequency, from AerotechAutomation1Enums import (
AxisDataSignal, PsoWindowInput, DriveDataCaptureInput, DriveDataCaptureTrigger, DataCollectionMode,
TaskDataSignal, SystemDataSignal, TomcatSequencerState) DataCollectionFrequency,
AxisDataSignal,
PsoWindowInput,
DriveDataCaptureInput,
DriveDataCaptureTrigger,
TaskDataSignal,
SystemDataSignal,
TomcatSequencerState,
)
from typing import Union from typing import Union
from collections import OrderedDict 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"
def __init__(self, prefix="", *, name, kind=None, read_attrs=None, configuration_attrs=None, parent=None, **kwargs): def __init__(
super().__init__(prefix=prefix, name=name, kind=kind, read_attrs=read_attrs, configuration_attrs=configuration_attrs, parent=parent, **kwargs) self,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
**kwargs,
):
super().__init__(
prefix=prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
**kwargs,
)
self._startPosition = None self._startPosition = None
self._targetPosition = None self._targetPosition = None
self.subscribe(self._progress_update, run=False) self.subscribe(self._progress_update, run=False)
def configure(self, d: dict): def configure(self, d: dict):
if "target" in d: if "target" in d:
self._targetPosition = d['target'] self._targetPosition = d["target"]
del d['target'] del d["target"]
if "position" in d: if "position" in d:
self._targetPosition = d['position'] self._targetPosition = d["position"]
del d['position'] del d["position"]
return super().configure(d) return super().configure(d)
def kickoff(self): def kickoff(self):
@ -53,19 +87,27 @@ class EpicsMotorX(EpicsMotor):
def _progress_update(self, value, **kwargs) -> None: def _progress_update(self, value, **kwargs) -> None:
"""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( sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1, ) self._run_subs(
sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1,
)
return return
progress = np.abs( (value-self._startPosition)/(self._targetPosition-self._startPosition) ) progress = np.abs(
(value - self._startPosition) / (self._targetPosition - self._startPosition)
)
max_value = 100 max_value = 100
self._run_subs( self._run_subs(
sub_type=self.SUB_PROGRESS, sub_type=self.SUB_PROGRESS,
value=int(100*progress), max_value=max_value, done=int(np.isclose(max_value, progress, 1e-3)), ) value=int(100 * progress),
max_value=max_value,
done=int(np.isclose(max_value, progress, 1e-3)),
)
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)
self._proc = EpicsSignal(read_pv + ".PROC", kind=Kind.omitted, put_complete=True) self._proc = EpicsSignal(read_pv + ".PROC", kind=Kind.omitted, put_complete=True)
@ -83,10 +125,9 @@ class EpicsPassiveRO(EpicsSignalRO):
return super().value return super().value
class aa1Controller(Device): class aa1Controller(Device):
"""Ophyd proxy class for the Aerotech Automation 1's core controller functionality""" """Ophyd proxy class for the Aerotech Automation 1's core controller functionality"""
controllername = Component(EpicsSignalRO, "NAME", kind=Kind.config) controllername = Component(EpicsSignalRO, "NAME", kind=Kind.config)
serialnumber = Component(EpicsSignalRO, "SN", kind=Kind.config) serialnumber = Component(EpicsSignalRO, "SN", kind=Kind.config)
apiversion = Component(EpicsSignalRO, "API_VERSION", kind=Kind.config) apiversion = Component(EpicsSignalRO, "API_VERSION", kind=Kind.config)
@ -94,8 +135,28 @@ class aa1Controller(Device):
taskcount = Component(EpicsSignalRO, "TASKCOUNT", kind=Kind.config) taskcount = Component(EpicsSignalRO, "TASKCOUNT", kind=Kind.config)
fastpoll = Component(EpicsSignalRO, "POLLTIME", auto_monitor=True, kind=Kind.hinted) fastpoll = Component(EpicsSignalRO, "POLLTIME", auto_monitor=True, kind=Kind.hinted)
slowpoll = Component(EpicsSignalRO, "DRVPOLLTIME", auto_monitor=True, kind=Kind.hinted) slowpoll = Component(EpicsSignalRO, "DRVPOLLTIME", auto_monitor=True, kind=Kind.hinted)
def __init__(self, prefix="", *, name, kind=None, read_attrs=None, configuration_attrs=None, parent=None, **kwargs):
super().__init__(prefix=prefix, name=name, kind=kind, read_attrs=read_attrs, configuration_attrs=configuration_attrs, parent=parent, **kwargs) def __init__(
self,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
**kwargs,
):
super().__init__(
prefix=prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
**kwargs,
)
class aa1Tasks(Device): class aa1Tasks(Device):
""" Task management API """ Task management API
@ -127,6 +188,7 @@ class aa1Tasks(Device):
''' '''
""" """
SUB_PROGRESS = "progress" SUB_PROGRESS = "progress"
_failure = Component(EpicsSignalRO, "FAILURE", auto_monitor=True, kind=Kind.hinted) _failure = Component(EpicsSignalRO, "FAILURE", auto_monitor=True, kind=Kind.hinted)
errStatus = Component(EpicsSignalRO, "ERRW", auto_monitor=True, kind=Kind.hinted) errStatus = Component(EpicsSignalRO, "ERRW", auto_monitor=True, kind=Kind.hinted)
@ -140,11 +202,31 @@ class aa1Tasks(Device):
fileName = Component(EpicsSignal, "FILENAME", string=True, kind=Kind.omitted, put_complete=True) fileName = Component(EpicsSignal, "FILENAME", string=True, kind=Kind.omitted, put_complete=True)
_fileRead = Component(EpicsPassiveRO, "FILEREAD", string=True, kind=Kind.omitted) _fileRead = Component(EpicsPassiveRO, "FILEREAD", string=True, kind=Kind.omitted)
_fileWrite = Component(EpicsSignal, "FILEWRITE", string=True, kind=Kind.omitted, put_complete=True) _fileWrite = Component(
EpicsSignal, "FILEWRITE", string=True, kind=Kind.omitted, put_complete=True
)
def __init__(self, prefix="", *, name, kind=None, read_attrs=None, configuration_attrs=None, parent=None, **kwargs): def __init__(
self,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
**kwargs,
):
""" __init__ MUST have a full argument list""" """ __init__ MUST have a full argument list"""
super().__init__(prefix=prefix, name=name, kind=kind, read_attrs=read_attrs, configuration_attrs=configuration_attrs, parent=parent, **kwargs) super().__init__(
prefix=prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
**kwargs,
)
self._currentTask = None self._currentTask = None
self._textToExecute = None self._textToExecute = None
self._isConfigured = False self._isConfigured = False
@ -154,7 +236,9 @@ class aa1Tasks(Device):
def _progress_update(self, value, **kwargs) -> None: def _progress_update(self, value, **kwargs) -> None:
"""Progress update on the scan""" """Progress update on the scan"""
value = self.progress() value = self.progress()
self._run_subs( sub_type=self.SUB_PROGRESS, value=value, max_value=1, done=1, ) self._run_subs(
sub_type=self.SUB_PROGRESS, value=value, max_value=1, done=1,
)
def _progress(self) -> None: def _progress(self) -> None:
"""Progress update on the scan""" """Progress update on the scan"""
@ -185,7 +269,7 @@ class aa1Tasks(Device):
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")
self.trigger().wait() self.trigger().wait()
print("Runscript waited") print("Runscript waited")
@ -194,7 +278,7 @@ class aa1Tasks(Device):
""" 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})
self.kickoff().wait() self.kickoff().wait()
if mode in [0, "None", None]: if mode in [0, "None", None]:
@ -207,12 +291,12 @@ class aa1Tasks(Device):
""" 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
taskIndex = int(d['taskIndex']) if 'taskIndex' in d else 4 taskIndex = int(d["taskIndex"]) if "taskIndex" in d else 4
settle_time = float(d['settle_time']) if 'settle_time' in d else None settle_time = float(d["settle_time"]) if "settle_time" in d else None
mode = d['mode'] if 'mode' in d else None mode = d["mode"] if "mode" in d else None
self._isStepConfig = d['stepper'] if 'stepper' in d else False self._isStepConfig = d["stepper"] if "stepper" in d else False
# Validation # Validation
if taskIndex < 1 or taskIndex > 31: if taskIndex < 1 or taskIndex > 31:
@ -302,6 +386,7 @@ class aa1Tasks(Device):
print("Called aa1Task.complete()") print("Called aa1Task.complete()")
timestamp_ = 0 timestamp_ = 0
taskIdx = int(self.taskIndex.get()) taskIdx = int(self.taskIndex.get())
def notRunning2(*args, old_value, value, timestamp, **kwargs): def notRunning2(*args, old_value, value, timestamp, **kwargs):
nonlocal timestamp_ nonlocal timestamp_
result = False if value[taskIdx] in ["Running", 4] else True result = False if value[taskIdx] in ["Running", 4] else True
@ -315,7 +400,14 @@ class aa1Tasks(Device):
def describe_collect(self) -> OrderedDict: def describe_collect(self) -> OrderedDict:
dd = OrderedDict() dd = OrderedDict()
dd['success'] = {'source': "internal", 'dtype': 'integer', 'shape': [], 'units': '', 'lower_ctrl_limit': 0, 'upper_ctrl_limit': 0} dd["success"] = {
"source": "internal",
"dtype": "integer",
"shape": [],
"units": "",
"lower_ctrl_limit": 0,
"upper_ctrl_limit": 0,
}
return {self.name: dd} return {self.name: dd}
def collect(self) -> OrderedDict: def collect(self) -> OrderedDict:
@ -331,6 +423,7 @@ class aa1TaskState(Device):
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.
""" """
index = Component(EpicsSignalRO, "INDEX", kind=Kind.config) index = Component(EpicsSignalRO, "INDEX", kind=Kind.config)
status = Component(EpicsSignalRO, "STATUS", auto_monitor=True, kind=Kind.hinted) status = Component(EpicsSignalRO, "STATUS", auto_monitor=True, kind=Kind.hinted)
errorCode = Component(EpicsSignalRO, "ERROR", auto_monitor=True, kind=Kind.hinted) errorCode = Component(EpicsSignalRO, "ERROR", auto_monitor=True, kind=Kind.hinted)
@ -341,6 +434,7 @@ class aa1TaskState(Device):
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
def notRunning(*args, old_value, value, timestamp, **kwargs): def notRunning(*args, old_value, value, timestamp, **kwargs):
nonlocal timestamp_ nonlocal timestamp_
result = False if (timestamp_ == 0) else (value not in ["Running", 4]) result = False if (timestamp_ == 0) else (value not in ["Running", 4])
@ -359,7 +453,14 @@ class aa1TaskState(Device):
def describe_collect(self) -> OrderedDict: def describe_collect(self) -> OrderedDict:
dd = OrderedDict() dd = OrderedDict()
dd['success'] = {'source': "internal", 'dtype': 'integer', 'shape': [], 'units': '', 'lower_ctrl_limit': 0, 'upper_ctrl_limit': 0} dd["success"] = {
"source": "internal",
"dtype": "integer",
"shape": [],
"units": "",
"lower_ctrl_limit": 0,
"upper_ctrl_limit": 0,
}
return dd return dd
def collect(self) -> OrderedDict: def collect(self) -> OrderedDict:
@ -381,6 +482,7 @@ class aa1DataAcquisition(Device):
3. Start your data collection 3. Start your data collection
4. Read back the recorded data with "readback" 4. Read back the recorded data with "readback"
""" """
# Status monitoring # Status monitoring
status = Component(EpicsSignalRO, "RUNNING", auto_monitor=True, kind=Kind.hinted) status = Component(EpicsSignalRO, "RUNNING", auto_monitor=True, kind=Kind.hinted)
points_max = Component(EpicsSignal, "MAXPOINTS", kind=Kind.config, put_complete=True) points_max = Component(EpicsSignal, "MAXPOINTS", kind=Kind.config, put_complete=True)
@ -486,7 +588,6 @@ class aa1DataAcquisition(Device):
return data return data
class aa1GlobalVariables(Device): class aa1GlobalVariables(Device):
""" Global variables """ Global variables
@ -505,6 +606,7 @@ class aa1GlobalVariables(Device):
ret_arr = var.readFloat(1000, 1024) ret_arr = var.readFloat(1000, 1024)
""" """
# Status monitoring # Status monitoring
num_real = Component(EpicsSignalRO, "NUM-REAL_RBV", kind=Kind.config) num_real = Component(EpicsSignalRO, "NUM-REAL_RBV", kind=Kind.config)
num_int = Component(EpicsSignalRO, "NUM-INT_RBV", kind=Kind.config) num_int = Component(EpicsSignalRO, "NUM-INT_RBV", kind=Kind.config)
@ -559,7 +661,6 @@ class aa1GlobalVariables(Device):
else: else:
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():
@ -613,7 +714,6 @@ class aa1GlobalVariables(Device):
raise RuntimeError("Unsupported string value type: {type(value)}") raise RuntimeError("Unsupported string value type: {type(value)}")
class aa1GlobalVariableBindings(Device): class aa1GlobalVariableBindings(Device):
""" Polled global variables """ Polled global variables
@ -621,30 +721,124 @@ class aa1GlobalVariableBindings(Device):
on the Automation1 controller. These variables are continuously polled on the Automation1 controller. These variables are continuously polled
and are thus a convenient way to interface scripts with the outside word. and are thus a convenient way to interface scripts with the outside word.
""" """
int0 = Component(EpicsSignalRO, "INT0_RBV", auto_monitor=True, name="int0", kind=Kind.hinted) int0 = Component(EpicsSignalRO, "INT0_RBV", auto_monitor=True, name="int0", kind=Kind.hinted)
int1 = Component(EpicsSignalRO, "INT1_RBV", auto_monitor=True, name="int1", kind=Kind.hinted) int1 = Component(EpicsSignalRO, "INT1_RBV", auto_monitor=True, name="int1", kind=Kind.hinted)
int2 = Component(EpicsSignalRO, "INT2_RBV", auto_monitor=True, name="int2", kind=Kind.hinted) int2 = Component(EpicsSignalRO, "INT2_RBV", auto_monitor=True, name="int2", kind=Kind.hinted)
int3 = Component(EpicsSignalRO, "INT3_RBV", auto_monitor=True, name="int3", kind=Kind.hinted) int3 = Component(EpicsSignalRO, "INT3_RBV", auto_monitor=True, name="int3", kind=Kind.hinted)
int8 = Component(EpicsSignal, "INT8_RBV", put_complete=True, write_pv="INT8", auto_monitor=True, name="int8", kind=Kind.hinted) int8 = Component(
int9 = Component(EpicsSignal, "INT9_RBV", put_complete=True, write_pv="INT9", auto_monitor=True, name="int9", kind=Kind.hinted) EpicsSignal,
int10 = Component(EpicsSignal, "INT10_RBV", put_complete=True, write_pv="INT10", auto_monitor=True, name="int10", kind=Kind.hinted) "INT8_RBV",
int11 = Component(EpicsSignal, "INT11_RBV", put_complete=True, write_pv="INT11", auto_monitor=True, name="int11", kind=Kind.hinted) put_complete=True,
write_pv="INT8",
auto_monitor=True,
name="int8",
kind=Kind.hinted,
)
int9 = Component(
EpicsSignal,
"INT9_RBV",
put_complete=True,
write_pv="INT9",
auto_monitor=True,
name="int9",
kind=Kind.hinted,
)
int10 = Component(
EpicsSignal,
"INT10_RBV",
put_complete=True,
write_pv="INT10",
auto_monitor=True,
name="int10",
kind=Kind.hinted,
)
int11 = Component(
EpicsSignal,
"INT11_RBV",
put_complete=True,
write_pv="INT11",
auto_monitor=True,
name="int11",
kind=Kind.hinted,
)
float0 = Component(EpicsSignalRO, "REAL0_RBV", auto_monitor=True, name="float0", kind=Kind.hinted) float0 = Component(
float1 = Component(EpicsSignalRO, "REAL1_RBV", auto_monitor=True, name="float1", kind=Kind.hinted) EpicsSignalRO, "REAL0_RBV", auto_monitor=True, name="float0", kind=Kind.hinted
float2 = Component(EpicsSignalRO, "REAL2_RBV", auto_monitor=True, name="float2", kind=Kind.hinted) )
float3 = Component(EpicsSignalRO, "REAL3_RBV", auto_monitor=True, name="float3", kind=Kind.hinted) float1 = Component(
float16 = Component(EpicsSignal, "REAL16_RBV", write_pv="REAL16", put_complete=True, auto_monitor=True, name="float16", kind=Kind.hinted) EpicsSignalRO, "REAL1_RBV", auto_monitor=True, name="float1", kind=Kind.hinted
float17 = Component(EpicsSignal, "REAL17_RBV", write_pv="REAL17", put_complete=True, auto_monitor=True, name="float17", kind=Kind.hinted) )
float18 = Component(EpicsSignal, "REAL18_RBV", write_pv="REAL18", put_complete=True, auto_monitor=True, name="float18", kind=Kind.hinted) float2 = Component(
float19 = Component(EpicsSignal, "REAL19_RBV", write_pv="REAL19", put_complete=True, auto_monitor=True, name="float19", kind=Kind.hinted) EpicsSignalRO, "REAL2_RBV", auto_monitor=True, name="float2", kind=Kind.hinted
)
float3 = Component(
EpicsSignalRO, "REAL3_RBV", auto_monitor=True, name="float3", kind=Kind.hinted
)
float16 = Component(
EpicsSignal,
"REAL16_RBV",
write_pv="REAL16",
put_complete=True,
auto_monitor=True,
name="float16",
kind=Kind.hinted,
)
float17 = Component(
EpicsSignal,
"REAL17_RBV",
write_pv="REAL17",
put_complete=True,
auto_monitor=True,
name="float17",
kind=Kind.hinted,
)
float18 = Component(
EpicsSignal,
"REAL18_RBV",
write_pv="REAL18",
put_complete=True,
auto_monitor=True,
name="float18",
kind=Kind.hinted,
)
float19 = Component(
EpicsSignal,
"REAL19_RBV",
write_pv="REAL19",
put_complete=True,
auto_monitor=True,
name="float19",
kind=Kind.hinted,
)
# BEC LiveTable crashes on non-numeric values # BEC LiveTable crashes on non-numeric values
str0 = Component(EpicsSignalRO, "STR0_RBV", auto_monitor=True, string=True, name="str0", kind=Kind.config) str0 = Component(
str1 = Component(EpicsSignalRO, "STR1_RBV", auto_monitor=True, string=True, name="str1", kind=Kind.config) EpicsSignalRO, "STR0_RBV", auto_monitor=True, string=True, name="str0", kind=Kind.config
str4 = Component(EpicsSignal, "STR4_RBV", put_complete=True, string=True, auto_monitor=True, write_pv="STR4", name="str4", kind=Kind.config) )
str5 = Component(EpicsSignal, "STR5_RBV", put_complete=True, string=True, auto_monitor=True, write_pv="STR5", name="str5", kind=Kind.config) str1 = Component(
EpicsSignalRO, "STR1_RBV", auto_monitor=True, string=True, name="str1", kind=Kind.config
)
str4 = Component(
EpicsSignal,
"STR4_RBV",
put_complete=True,
string=True,
auto_monitor=True,
write_pv="STR4",
name="str4",
kind=Kind.config,
)
str5 = Component(
EpicsSignal,
"STR5_RBV",
put_complete=True,
string=True,
auto_monitor=True,
write_pv="STR5",
name="str5",
kind=Kind.config,
)
class aa1AxisIo(Device): class aa1AxisIo(Device):
@ -655,6 +849,7 @@ class aa1AxisIo(Device):
should be done in AeroScript. Only one pin can be writen directly but should be done in AeroScript. Only one pin can be writen directly but
several can be polled! several can be polled!
""" """
polllvl = Component(EpicsSignal, "POLLLVL", put_complete=True, kind=Kind.config) polllvl = Component(EpicsSignal, "POLLLVL", put_complete=True, kind=Kind.config)
ai0 = Component(EpicsSignalRO, "AI0-RBV", auto_monitor=True, kind=Kind.hinted) ai0 = Component(EpicsSignalRO, "AI0-RBV", auto_monitor=True, kind=Kind.hinted)
ai1 = Component(EpicsSignalRO, "AI1-RBV", auto_monitor=True, kind=Kind.hinted) ai1 = Component(EpicsSignalRO, "AI1-RBV", auto_monitor=True, kind=Kind.hinted)
@ -692,7 +887,6 @@ class aa1AxisIo(Device):
self.do.set(value, settle_time=settle_time).wait() self.do.set(value, settle_time=settle_time).wait()
class aa1AxisPsoBase(Device): class aa1AxisPsoBase(Device):
""" Position Sensitive Output - Base class """ Position Sensitive Output - Base class
@ -708,6 +902,7 @@ class aa1AxisPsoBase(Device):
Specific operation modes should be implemented in child classes. Specific operation modes should be implemented in child classes.
""" """
# ######################################################################## # ########################################################################
# General module status # General module status
status = Component(EpicsSignalRO, "STATUS", auto_monitor=True, kind=Kind.hinted) status = Component(EpicsSignalRO, "STATUS", auto_monitor=True, kind=Kind.hinted)
@ -722,7 +917,9 @@ class aa1AxisPsoBase(Device):
dstCounterEna = Component(EpicsSignal, "DIST:COUNTER", put_complete=True, kind=Kind.omitted) dstCounterEna = Component(EpicsSignal, "DIST:COUNTER", put_complete=True, kind=Kind.omitted)
dstCounterVal = Component(EpicsSignalRO, "DIST:CTR0_RBV", auto_monitor=True, kind=Kind.hinted) dstCounterVal = Component(EpicsSignalRO, "DIST:CTR0_RBV", auto_monitor=True, kind=Kind.hinted)
dstArrayIdx = Component(EpicsSignalRO, "DIST:IDX_RBV", auto_monitor=True, kind=Kind.hinted) dstArrayIdx = Component(EpicsSignalRO, "DIST:IDX_RBV", auto_monitor=True, kind=Kind.hinted)
dstArrayDepleted = Component(EpicsSignalRO, "DIST:ARRAY-DEPLETED-RBV", auto_monitor=True, kind=Kind.hinted) dstArrayDepleted = Component(
EpicsSignalRO, "DIST:ARRAY-DEPLETED-RBV", auto_monitor=True, kind=Kind.hinted
)
dstDirection = Component(EpicsSignal, "DIST:EVENTDIR", put_complete=True, kind=Kind.omitted) dstDirection = Component(EpicsSignal, "DIST:EVENTDIR", put_complete=True, kind=Kind.omitted)
dstDistance = Component(EpicsSignal, "DIST:DISTANCE", put_complete=True, kind=Kind.hinted) dstDistance = Component(EpicsSignal, "DIST:DISTANCE", put_complete=True, kind=Kind.hinted)
@ -767,9 +964,6 @@ class aa1AxisPsoBase(Device):
self.waveMode.set(orig_waveMode).wait() self.waveMode.set(orig_waveMode).wait()
class aa1AxisPsoDistance(aa1AxisPsoBase): class aa1AxisPsoDistance(aa1AxisPsoBase):
""" Position Sensitive Output - Distance mode """ Position Sensitive Output - Distance mode
@ -802,11 +996,30 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
pso.configure(d={'distance': 1.8, 'wmode': "pulsed", 'n_pulse': 5}) pso.configure(d={'distance': 1.8, 'wmode': "pulsed", 'n_pulse': 5})
pso.kickoff().wait() pso.kickoff().wait()
""" """
SUB_PROGRESS = "progress" SUB_PROGRESS = "progress"
def __init__(self, prefix="", *, name, kind=None, read_attrs=None, configuration_attrs=None, parent=None, **kwargs): def __init__(
self,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
**kwargs,
):
""" __init__ MUST have a full argument list""" """ __init__ MUST have a full argument list"""
super().__init__(prefix=prefix, name=name, kind=kind, read_attrs=read_attrs, configuration_attrs=configuration_attrs, parent=parent, **kwargs) super().__init__(
prefix=prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
**kwargs,
)
self._Vdistance = 3.141592 self._Vdistance = 3.141592
self.subscribe(self._progress_update, "progress", run=False) self.subscribe(self._progress_update, "progress", run=False)
@ -814,7 +1027,9 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
"""Progress update on the scan""" """Progress update on the scan"""
if self.dstArrayDepleted.value: if self.dstArrayDepleted.value:
print("PSO array depleted") print("PSO array depleted")
self._run_subs( sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1, ) self._run_subs(
sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1,
)
return return
progress = 1 progress = 1
@ -822,7 +1037,10 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
print(f"PSO array proggress: {progress}") print(f"PSO array proggress: {progress}")
self._run_subs( self._run_subs(
sub_type=self.SUB_PROGRESS, sub_type=self.SUB_PROGRESS,
value=int(progress), max_value=max_value, done=int(np.isclose(max_value, progress, 1e-3)), ) value=int(progress),
max_value=max_value,
done=int(np.isclose(max_value, progress, 1e-3)),
)
# ######################################################################## # ########################################################################
# PSO high level interface # PSO high level interface
@ -833,12 +1051,11 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
: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.
:param wmode: Waveform mode configuration, usually pulsed/toggled. :param wmode: Waveform mode configuration, usually pulsed/toggled.
""" """
distance = d['distance'] distance = d["distance"]
wmode = str(d['wmode']) wmode = str(d["wmode"])
t_pulse = float(d['t_pulse']) if 't_pulse' in d else 100 t_pulse = float(d["t_pulse"]) if "t_pulse" in d else 100
w_pulse = float(d['w_pulse']) if 'w_pulse' in d else 200 w_pulse = float(d["w_pulse"]) if "w_pulse" in d else 200
n_pulse = float(d['n_pulse']) if 'n_pulse' in d else 1 n_pulse = float(d["n_pulse"]) if "n_pulse" in d else 1
# Validate input parameters # Validate input parameters
if wmode not in ["pulse", "pulsed", "toggle", "toggled"]: if wmode not in ["pulse", "pulsed", "toggle", "toggled"]:
@ -908,11 +1125,14 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
self.dstEventsEna.set("Off").wait() self.dstEventsEna.set("Off").wait()
self.dstCounterEna.set("Off").wait() self.dstCounterEna.set("Off").wait()
return super().unstage() return super().unstage()
# ######################################################################## # ########################################################################
# Bluesky flyer interface # Bluesky flyer interface
def kickoff(self) -> DeviceStatus: def kickoff(self) -> DeviceStatus:
# Rearm the configured array # Rearm the configured array
if hasattr(self, "_distanceValue") and isinstance(self._distanceValue, (np.ndarray, list, tuple)): if hasattr(self, "_distanceValue") and isinstance(
self._distanceValue, (np.ndarray, list, tuple)
):
self.dstArrayRearm.set(1).wait() self.dstArrayRearm.set(1).wait()
# Start monitoring the counters # Start monitoring the counters
self.dstEventsEna.set("On").wait() self.dstEventsEna.set("On").wait()
@ -925,9 +1145,12 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
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(self._distanceValue, (np.ndarray, list, tuple)): if hasattr(self, "_distanceValue") and isinstance(
self._distanceValue, (np.ndarray, list, tuple)
):
# 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 notRunning(*args, old_value, value, timestamp, **kwargs): def notRunning(*args, old_value, value, timestamp, **kwargs):
nonlocal timestamp_ nonlocal timestamp_
result = False if (timestamp_ == 0) else bool(int(value) & 0x1000) result = False if (timestamp_ == 0) else bool(int(value) & 0x1000)
@ -948,7 +1171,14 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
def describe_collect(self) -> OrderedDict: def describe_collect(self) -> OrderedDict:
ret = OrderedDict() ret = OrderedDict()
ret['index'] = {'source': "internal", 'dtype': 'integer', 'shape': [], 'units': '', 'lower_ctrl_limit': 0, 'upper_ctrl_limit': 0} ret["index"] = {
"source": "internal",
"dtype": "integer",
"shape": [],
"units": "",
"lower_ctrl_limit": 0,
"upper_ctrl_limit": 0,
}
return {self.name: ret} return {self.name: ret}
def collect(self) -> OrderedDict: def collect(self) -> OrderedDict:
@ -958,8 +1188,6 @@ class aa1AxisPsoDistance(aa1AxisPsoBase):
yield ret yield ret
class aa1AxisPsoWindow(aa1AxisPsoBase): class aa1AxisPsoWindow(aa1AxisPsoBase):
""" Position Sensitive Output - Window mode """ Position Sensitive Output - Window mode
@ -979,9 +1207,28 @@ class aa1AxisPsoWindow(aa1AxisPsoBase):
For a more detailed description of additional signals and masking plase For a more detailed description of additional signals and masking plase
refer to Automation1's online manual. refer to Automation1's online manual.
""" """
def __init__(self, prefix="", *, name, kind=None, read_attrs=None, configuration_attrs=None, parent=None, **kwargs):
def __init__(
self,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
**kwargs,
):
""" __init__ MUST have a full argument list""" """ __init__ MUST have a full argument list"""
super().__init__(prefix=prefix, name=name, kind=kind, read_attrs=read_attrs, configuration_attrs=configuration_attrs, parent=parent, **kwargs) super().__init__(
prefix=prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
**kwargs,
)
self._mode = "output" self._mode = "output"
self._eventMode = "Enter" self._eventMode = "Enter"
@ -994,12 +1241,12 @@ class aa1AxisPsoWindow(aa1AxisPsoBase):
: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.
:param wmode: Waveform mode configuration, usually output/pulsed/toggled. :param wmode: Waveform mode configuration, usually output/pulsed/toggled.
""" """
bounds = d['distance'] bounds = d["distance"]
wmode = str(d['wmode']) wmode = str(d["wmode"])
emode = str(d['emode']) emode = str(d["emode"])
t_pulse = float(d['t_pulse']) if 't_pulse' in d else None t_pulse = float(d["t_pulse"]) if "t_pulse" in d else None
w_pulse = float(d['w_pulse']) if 'w_pulse' in d else None w_pulse = float(d["w_pulse"]) if "w_pulse" in d else None
n_pulse = float(d['n_pulse']) if 'n_pulse' in d else None n_pulse = float(d["n_pulse"]) if "n_pulse" in d else None
# Validate input parameters # Validate input parameters
if wmode not in ["pulse", "pulsed", "toggle", "toggled", "output", "flag"]: if wmode not in ["pulse", "pulsed", "toggle", "toggled", "output", "flag"]:
@ -1080,11 +1327,6 @@ class aa1AxisPsoWindow(aa1AxisPsoBase):
return super().unstage() return super().unstage()
class aa1AxisDriveDataCollection(Device): class aa1AxisDriveDataCollection(Device):
""" Axis data collection """ Axis data collection
@ -1125,29 +1367,50 @@ class aa1AxisDriveDataCollection(Device):
SUB_PROGRESS = "progress" SUB_PROGRESS = "progress"
def __init__(self, prefix="", *, name, kind=None, read_attrs=None, configuration_attrs=None, parent=None, **kwargs): def __init__(
super().__init__(prefix=prefix, name=name, kind=kind, read_attrs=read_attrs, configuration_attrs=configuration_attrs, parent=parent, **kwargs) self,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
**kwargs,
):
super().__init__(
prefix=prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
**kwargs,
)
self.subscribe(self._progress_update, "progress", run=False) self.subscribe(self._progress_update, "progress", run=False)
def _progress_update(self, value, **kwargs) -> None: def _progress_update(self, value, **kwargs) -> None:
"""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( sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1, ) self._run_subs(
sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1,
)
return return
progress = 1 progress = 1
max_value = 1 max_value = 1
self._run_subs( self._run_subs(
sub_type=self.SUB_PROGRESS, sub_type=self.SUB_PROGRESS,
value=int(progress), max_value=max_value, done=int(np.isclose(max_value, progress, 1e-3)), ) value=int(progress),
max_value=max_value,
done=int(np.isclose(max_value, progress, 1e-3)),
)
def configure(self, d: dict = {}) -> tuple: def configure(self, d: dict = {}) -> tuple:
npoints = int(d['npoints']) npoints = int(d["npoints"])
trigger = int(d['trigger']) if 'trigger' in d else DriveDataCaptureTrigger.PsoOutput trigger = int(d["trigger"]) if "trigger" in d else DriveDataCaptureTrigger.PsoOutput
source0 = int(d['source0']) if 'source0' in d else DriveDataCaptureInput.PrimaryFeedback source0 = int(d["source0"]) if "source0" in d else DriveDataCaptureInput.PrimaryFeedback
source1 = int(d['source1']) if 'source1' in d else DriveDataCaptureInput.PositionCommand source1 = int(d["source1"]) if "source1" in d else DriveDataCaptureInput.PositionCommand
old = self.read_configuration() old = self.read_configuration()
@ -1194,6 +1457,7 @@ class aa1AxisDriveDataCollection(Device):
# 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 negEdge(*args, old_value, value, timestamp, **kwargs): def negEdge(*args, old_value, value, timestamp, **kwargs):
nonlocal timestamp_ nonlocal timestamp_
result = False if (timestamp_ == 0) else (old_value == 1 and value == 0) result = False if (timestamp_ == 0) else (old_value == 1 and value == 0)
@ -1212,11 +1476,24 @@ class aa1AxisDriveDataCollection(Device):
status.wait() status.wait()
return status return status
def describe_collect(self) -> OrderedDict: def describe_collect(self) -> OrderedDict:
ret = OrderedDict() ret = OrderedDict()
ret['buffer0'] = {'source': "internal", 'dtype': 'array', 'shape': [], 'units': '', 'lower_ctrl_limit': 0, 'upper_ctrl_limit': 0} ret["buffer0"] = {
ret['buffer1'] = {'source': "internal", 'dtype': 'array', 'shape': [], 'units': '', 'lower_ctrl_limit': 0, 'upper_ctrl_limit': 0} "source": "internal",
"dtype": "array",
"shape": [],
"units": "",
"lower_ctrl_limit": 0,
"upper_ctrl_limit": 0,
}
ret["buffer1"] = {
"source": "internal",
"dtype": "array",
"shape": [],
"units": "",
"lower_ctrl_limit": 0,
"upper_ctrl_limit": 0,
}
return {self.name: ret} return {self.name: ret}
def collect(self) -> OrderedDict: def collect(self) -> OrderedDict:
@ -1232,9 +1509,6 @@ class aa1AxisDriveDataCollection(Device):
yield ret yield ret
# Automatically start simulation if directly invoked # Automatically start simulation if directly invoked
if __name__ == "__main__": if __name__ == "__main__":
AA1_IOC_NAME = "X02DA-ES1-SMP1" AA1_IOC_NAME = "X02DA-ES1-SMP1"
@ -1250,6 +1524,3 @@ if __name__ == "__main__":
globb.describe() globb.describe()
mot = EpicsMotor(AA1_IOC_NAME + ":" + AA1_AXIS_NAME, name="x") mot = EpicsMotor(AA1_IOC_NAME + ":" + AA1_AXIS_NAME, name="x")
mot.wait_for_connection() mot.wait_for_connection()

View File

@ -383,9 +383,6 @@ class TaskDataSignal:
CommandQueueExecutedCount = 424 CommandQueueExecutedCount = 424
class SystemDataSignal: class SystemDataSignal:
VirtualBinaryInput = 46 VirtualBinaryInput = 46
VirtualBinaryOutput = 47 VirtualBinaryOutput = 47
@ -442,7 +439,6 @@ class SystemDataSignal:
ModbusServerEnabled = 439 ModbusServerEnabled = 439
class DataCollectionFrequency: class DataCollectionFrequency:
Undefined = 0 Undefined = 0
Fixed1kHz = 1 Fixed1kHz = 1
@ -482,11 +478,12 @@ class PsoWindowInput:
XC4eSyncPortA = 137 XC4eSyncPortA = 137
XC4eSyncPortB = 138 XC4eSyncPortB = 138
XC4eDrivePulseStream = 139 XC4eDrivePulseStream = 139
XL5ePrimaryFeedback = 145, XL5ePrimaryFeedback = (145,)
XL5eAuxiliaryFeedback = 146, XL5eAuxiliaryFeedback = (146,)
XL5eSyncPortA = 147, XL5eSyncPortA = (147,)
XL5eSyncPortB = 148, XL5eSyncPortB = (148,)
XL5eDrivePulseStream = 149, XL5eDrivePulseStream = (149,)
# @brief Specifies the PSO output pin settings for each drive. # @brief Specifies the PSO output pin settings for each drive.
class XC4ePsoOutputPin: class XC4ePsoOutputPin:
@ -494,6 +491,7 @@ class XC4ePsoOutputPin:
AuxiliaryMarkerDifferential = 112 AuxiliaryMarkerDifferential = 112
AuxiliaryMarkerSingleEnded = 113 AuxiliaryMarkerSingleEnded = 113
class XC4PsoOutputPin: class XC4PsoOutputPin:
DedicatedOutput = 108 DedicatedOutput = 108
AuxiliaryMarkerDifferential = 109 AuxiliaryMarkerDifferential = 109
@ -854,61 +852,50 @@ class DriveDataCaptureTrigger:
AuxiliaryMarkerFallingEdge = 7 AuxiliaryMarkerFallingEdge = 7
class PsoOutputPin: class PsoOutputPin:
GL4None = 100, GL4None = (100,)
GL4LaserOutput0 = 101, GL4LaserOutput0 = (101,)
XL4sNone = 102, XL4sNone = (102,)
XL4sLaserOutput0 = 103, XL4sLaserOutput0 = (103,)
XR3None = 104, XR3None = (104,)
XR3PsoOutput1 = 105, XR3PsoOutput1 = (105,)
XR3PsoOutput2 = 106, XR3PsoOutput2 = (106,)
XR3PsoOutput3 = 107, XR3PsoOutput3 = (107,)
XC4DedicatedOutput = 108, XC4DedicatedOutput = (108,)
XC4AuxiliaryMarkerDifferential = 109, XC4AuxiliaryMarkerDifferential = (109,)
XC4AuxiliaryMarkerSingleEnded = 110, XC4AuxiliaryMarkerSingleEnded = (110,)
XC4eDedicatedOutput = 111, XC4eDedicatedOutput = (111,)
XC4eAuxiliaryMarkerDifferential = 112, XC4eAuxiliaryMarkerDifferential = (112,)
XC4eAuxiliaryMarkerSingleEnded = 113, XC4eAuxiliaryMarkerSingleEnded = (113,)
XC6eDedicatedOutput = 114, XC6eDedicatedOutput = (114,)
XC6eAuxiliaryMarkerDifferential = 115, XC6eAuxiliaryMarkerDifferential = (115,)
XC6eAuxiliaryMarkerSingleEnded = 116, XC6eAuxiliaryMarkerSingleEnded = (116,)
XL5eDedicatedOutput = 117, XL5eDedicatedOutput = (117,)
XL5eAuxiliaryMarkerDifferential = 118, XL5eAuxiliaryMarkerDifferential = (118,)
XL5eAuxiliaryMarkerSingleEnded = 119, XL5eAuxiliaryMarkerSingleEnded = (119,)
XC2DedicatedOutput = 120, XC2DedicatedOutput = (120,)
XC2eDedicatedOutput = 121, XC2eDedicatedOutput = (121,)
XL2eDedicatedOutput = 122, XL2eDedicatedOutput = (122,)
XI4DedicatedOutput = 123, XI4DedicatedOutput = (123,)
iXC4DedicatedOutput = 124, iXC4DedicatedOutput = (124,)
iXC4AuxiliaryMarkerDifferential = 125, iXC4AuxiliaryMarkerDifferential = (125,)
iXC4AuxiliaryMarkerSingleEnded = 126, iXC4AuxiliaryMarkerSingleEnded = (126,)
iXC4eDedicatedOutput = 127, iXC4eDedicatedOutput = (127,)
iXC4eAuxiliaryMarkerDifferential = 128, iXC4eAuxiliaryMarkerDifferential = (128,)
iXC4eAuxiliaryMarkerSingleEnded = 129, iXC4eAuxiliaryMarkerSingleEnded = (129,)
iXC6eDedicatedOutput = 130, iXC6eDedicatedOutput = (130,)
iXC6eAuxiliaryMarkerDifferential = 131, iXC6eAuxiliaryMarkerDifferential = (131,)
iXC6eAuxiliaryMarkerSingleEnded = 132, iXC6eAuxiliaryMarkerSingleEnded = (132,)
iXL5eDedicatedOutput = 133, iXL5eDedicatedOutput = (133,)
iXL5eAuxiliaryMarkerDifferential = 134, iXL5eAuxiliaryMarkerDifferential = (134,)
iXL5eAuxiliaryMarkerSingleEnded = 135, iXL5eAuxiliaryMarkerSingleEnded = (135,)
iXR3None = 136, iXR3None = (136,)
iXR3PsoOutput1 = 137, iXR3PsoOutput1 = (137,)
iXR3PsoOutput2 = 138, iXR3PsoOutput2 = (138,)
iXR3PsoOutput3 = 139, iXR3PsoOutput3 = (139,)
GI4None = 140, GI4None = (140,)
GI4LaserOutput0 = 141, GI4LaserOutput0 = (141,)
iXC2DedicatedOutput = 142, iXC2DedicatedOutput = (142,)
iXC2eDedicatedOutput = 143, iXC2eDedicatedOutput = (143,)
iXL2eDedicatedOutput = 144, iXL2eDedicatedOutput = (144,)
iXI4DedicatedOutput = 145, iXI4DedicatedOutput = (145,)