diff --git a/ophyd_devices/epics/db/office_teststand_bec_config.yaml b/ophyd_devices/epics/db/office_teststand_bec_config.yaml new file mode 100644 index 0000000..92be2bb --- /dev/null +++ b/ophyd_devices/epics/db/office_teststand_bec_config.yaml @@ -0,0 +1,28 @@ +es1_roty: + description: 'Test rotation stage' + deviceClass: EpicsMotor + deviceConfig: {prefix: 'X02DA-ES1-SMP1:ROTY'} + onFailure: buffer + enabled: true + readoutPriority: monitored + +es1_aa1: + description: 'Rotation stage controller status' + deviceClass: epics:devices:aa1Controller + deviceConfig: {prefix: 'X02DA-ES1-SMP1:CTRL:'} + onFailure: buffer + enabled: true + readoutPriority: monitored + + +es1_aa1Tasks: + description: 'AA1 task management' + deviceClass: epics:devices:aa1Tasks + deviceConfig: {prefix: 'X02DA-ES1-SMP1:TASK:'} + onFailure: buffer + enabled: true + readoutPriority: monitored + + + + diff --git a/ophyd_devices/epics/devices/AerotechAutomation1.py b/ophyd_devices/epics/devices/AerotechAutomation1.py new file mode 100644 index 0000000..3cd4c57 --- /dev/null +++ b/ophyd_devices/epics/devices/AerotechAutomation1.py @@ -0,0 +1,1110 @@ +from ophyd import Device, Component, EpicsMotor, EpicsSignal, EpicsSignalRO, Kind, DerivedSignal +from ophyd.status import Status, SubscriptionStatus, StatusBase +from time import sleep +import warnings +import numpy as np +import time + +from .AerotechAutomation1Enums import (DataCollectionMode, DataCollectionFrequency, + AxisDataSignal, PsoWindowInput, DriveDataCaptureInput, DriveDataCaptureTrigger, + TaskDataSignal, SystemDataSignal, TomcatSequencerState) + +from .AerotechAutomation1Enums import * +from typing import Union +from collections import OrderedDict + + +def epicsCharArray2String(raw: bytes) -> str: + """ Ophyd might convert strings to uint16 vector...""" + if isinstance(raw, np.ndarray): + raw = raw[:-1] if raw[-1]==0 else raw + sReply = ''.join(str(s, encoding='ASCII') for s in raw.astype(np.uint8)) + print(f"Raw reply: {raw}\nConverted reply: {sReply}") + return sReply + else: + return raw + + + + +class EpicsSignalPassive(Device): + value = Component(EpicsSignalRO, "", kind=Kind.hinted) + proc = Component(EpicsSignal, ".PROC", kind=Kind.omitted, put_complete=True) + def get(self): + self.proc.set(1).wait() + return self.value.get() + + +class EpicsStringPassiveRO(EpicsSignalRO): + """Special helper class to work around a bug in ophyd (caproto backend) + that reads CHAR array strigs as uint16 arrays. + """ + + def __init__(self, read_pv, *, string=False, name=None, **kwargs): + super().__init__(read_pv, string=string, name=name, **kwargs) + self._proc = EpicsSignal(read_pv+".PROC", kind=Kind.omitted, put_complete=True) + + def wait_for_connection(self, *args, **kwargs): + super().wait_for_connection(*args, **kwargs) + self._proc.wait_for_connection(*args, **kwargs) + + def get(self, *args, **kwargs): + self._proc.set(1).wait() + raw = super().get(*args, **kwargs) + return epicsCharArray2String(raw) + + @property + def value(self): + raw = super().value + return epicsCharArray2String(raw) + + +class EpicsStringPassive(EpicsSignal): + """Special helper class to work around a bug in ophyd (caproto backend) + that reads CHAR array strigs as uint16 arrays. + """ + def __init__(self, read_pv, write_pv=None, *, put_complete=False, + string=False, limits=False, name=None, **kwargs): + super().__init__(read_pv, write_pv, put_complete, string, limits, name, **kwargs) + self._proc = EpicsSignal(read_pv+".PROC", kind=Kind.omitted, put_complete=True) + + def wait_for_connection(self, *args, **kwargs): + super().wait_for_connection(*args, **kwargs) + self._proc.wait_for_connection(*args, **kwargs) + + def get(self, *args, **kwargs): + self._proc.set(1).wait() + raw = super().get(*args, **kwargs) + return epicsCharArray2String(raw) + + @property + def value(self): + raw = super().value + return epicsCharArray2String(raw) + + +class EpicsString(EpicsSignal): + """Special helper class to work around a bug in ophyd (caproto backend) + that reads CHAR array strigs as uint16 arrays. + """ + def get(self, *args, **kwargs): + raw = super().get(*args, **kwargs) + return epicsCharArray2String(raw) + + @property + def value(self): + raw = super().value + return epicsCharArray2String(raw) + + +class EpicsStringRO(EpicsSignalRO): + """Special helper class to work around a bug in ophyd (caproto backend) + that reads CHAR array strigs as uint16 arrays. + """ + def get(self, *args, **kwargs): + raw = super().get(*args, **kwargs) + return epicsCharArray2String(raw) + + @property + def value(self): + raw = super().value + return epicsCharArray2String(raw) + + +class aa1Controller(Device): + """Ophyd proxy class for the Aerotech Automation 1's core controller functionality""" + controllername = Component(EpicsSignalRO, "NAME", kind=Kind.config) + serialnumber = Component(EpicsSignalRO, "SN", kind=Kind.config) + apiversion = Component(EpicsSignalRO, "API_VERSION", kind=Kind.config) + axiscount = Component(EpicsSignalRO, "AXISCOUNT", kind=Kind.config) + taskcount = Component(EpicsSignalRO, "TASKCOUNT", kind=Kind.config) + fastpoll = Component(EpicsSignalRO, "RUNNING", auto_monitor=True, kind=Kind.hinted) + slowpoll = Component(EpicsSignalRO, "RUNNING", 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) + +class aa1Tasks(Device): + """ Task management API + + The place to manage tasks and AeroScript user files on the controller. + You can read/write/compile/execute AeroScript files and also retrieve + saved data files from the controller. + + Execute does not require to store the script in a file, it will compile + it and run it directly on a certain thread. But there's no way to + retrieve the source code. + """ + _failure = Component(EpicsSignalRO, "FAILURE", kind=Kind.hinted) + errStatus = Component(EpicsSignalRO, "ERRW", auto_monitor=True, kind=Kind.hinted) + warnStatus = Component(EpicsSignalRO, "WARNW", auto_monitor=True, kind=Kind.hinted) + taskIndex = Component(EpicsSignal, "TASKIDX", kind=Kind.config, put_complete=True) + switch = Component(EpicsSignal, "SWITCH", kind=Kind.config, put_complete=True) + _execute = Component(EpicsSignal, "EXECUTE", kind=Kind.config, put_complete=True) + _executeMode = Component(EpicsSignal, "EXECUTE-MODE", kind=Kind.config, put_complete=True) + _executeReply = Component(EpicsSignalRO, "EXECUTE_RBV", auto_monitor=True) + + fileName = Component(EpicsSignal, "FILENAME", kind=Kind.config, put_complete=True) + _fileList = Component(EpicsSignalPassive, "FILELIST", kind=Kind.config) + _fileRead = Component(EpicsSignalPassive, "FILEREAD", kind=Kind.config) + _fileWrite = Component(EpicsSignal, "FILEWRITE", kind=Kind.config, put_complete=True) + + def __init__(self, prefix="", *, name, kind=None, read_attrs=None, configuration_attrs=None, parent=None, **kwargs): + """ __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) + self._currentTaskMonitor = None + self._textToExecute = None + + def listFiles(self) -> list: + """ List all available files on the controller """ + # Have to use CHAR array due to EPICS LSI bug... + namesbytes = self._fileList.get().astype(np.uint8).tobytes() + nameslist = namesbytes.decode('ascii').split('\t') + return nameslist + + def readFile(self, filename: str) -> str: + """ Read a file from the controller """ + # Have to use CHAR array due to EPICS LSI bug... + self.fileName.set(filename).wait() + filebytes = self._fileRead.get().astype(np.uint8).tobytes() + # C-strings terminate with trailing zero + if filebytes[-1]==0: + filebytes = filebytes[:-1] + filetext = filebytes.decode('ascii') + return filetext + + def writeFile(self, filename: str, filetext: str) -> None: + """ Write a file to the controller """ + self.fileName.set(filename).wait() + self._fileWrite.set(filetext).wait() + + 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""" + + self.configure(text=filetext, filename=filename, taskIndex=taskIndex) + self.trigger(settle_time=settle_time).wait() + + def execute(self, text: str, taskIndex: int=3, mode: str=0, settle_time=0.5): + """ Run a short text command on the Automation1 controller""" + + print(f"Executing program on task: {taskIndex}") + self.configure(text=text, taskIndex=taskIndex, mode=mode) + self.trigger(settle_time=settle_time).wait() + + if mode in [0, "None", None]: + return None + else: + raw = self._executeReply.get() + return epicsCharArray2String(raw) + + def configure(self, text: str=None, filename: str=None, taskIndex: int=4, settle_time=None, **kwargs): + """ Interface for configuration """ + # Validation + if taskIndex < 0 or taskIndex > 31: + raise RuntimeError(f"Invalid task index: {taskIndex}") + if (text is None) and (filename is None): + raise RuntimeError("Task execution requires either AeroScript text or filename") + if 'mode' in kwargs: + if kwargs['mode'] not in [0, 1, 2, 3, 4, "None", "Axis", "Int", "Double", "String", None]: + raise RuntimeError(f"Unknown execution mode: {kwargs['mode']}") + if kwargs['mode'] is None: + kwargs['mode'] = "None" + else: + kwargs['mode'] = "None" + + # common operations + self.taskIndex.set(taskIndex).wait() + self._textToExecute = None + #self._currentTaskMonitor = aa1TaskState() + + # Choose the right execution mode + if (filename is None) and (text not in [None, ""]): + # Direct command execution + print("Preparing for direct command execution") + self._executeMode.set(kwargs['mode']).wait() + self._textToExecute = text + elif (filename is not None) and (text in [None, ""]): + # Execute existing file + self.fileName.set(filename).wait() + self.switch.set("Load").wait() + elif (filename is not None) and (text not in [None, ""]): + print("Preparing to execute via intermediary file") + # Execute text via intermediate file + self.taskIndex.set(taskIndex).wait() + self.fileName.set(filename).wait() + self._fileWrite.set(text).wait() + self.switch.set("Load").wait() + self._textToExecute = None + else: + raise RuntimeError("Unsupported filename-text combo") + + ########################################################################## + # Bluesky stepper interface + def stage(self) -> None: + """ Default staging """ + super().stage() + def unstage(self) -> None: + """ Default unstaging """ + super().unstage() + + def trigger(self, settle_time=0.2) -> StatusBase: + """ Execute the script on the configured task""" + if self._textToExecute is not None: + status = self._execute.set(self._textToExecute, settle_time=settle_time) + else: + status = self.switch.set("Run", settle_time=settle_time) + return status + + def stop(self): + """ Stop the currently selected task """ + self.switch.set("Stop").wait() + + ########################################################################## + # Flyer interface + def kickoff(self) -> StatusBase: + """ Execute the script on the configured task""" + if self._textToExecute is not None: + status = self._execute.set(self._textToExecute, settle_time=0.5) + else: + status = self.switch.set("Run", settle_time=0.1) + return status + + def complete(self) -> StatusBase: + """ Execute the script on the configured task""" + #return self._currentTaskMonitor.complete() + status = StatusBase() + status.set_finished() + return status + + def describe_collect(self) -> OrderedDict: + dd = OrderedDict() + dd['success'] = {'source': "internal", 'dtype': 'integer', 'shape': [], 'units': '', 'lower_ctrl_limit': 0, 'upper_ctrl_limit': 0} + return {self.name: dd} + + def collect(self) -> OrderedDict: + ret = OrderedDict() + ret["timestamps"] = {"success": time.time()} + ret["data"] = {"success": 1} + yield ret + + +class aa1TaskState(Device): + """ Task state monitoring API + + This is the task state monitoring interface for Automation1 tasks. It + does not launch execution, but can wait for the execution to complete. + """ + index = Component(EpicsSignalRO, "INDEX", kind=Kind.config) + status = Component(EpicsSignalRO, "STATUS", auto_monitor=True, kind=Kind.hinted) + errorCode = Component(EpicsSignalRO, "ERROR", auto_monitor=True, kind=Kind.hinted) + warnCode = Component(EpicsSignalRO, "WARNING", auto_monitor=True, kind=Kind.hinted) + + def complete(self) -> StatusBase: + """ Bluesky flyer interface""" + # Define wait until the busy flag goes down (excluding initial update) + timestamp_ = 0 + def notRunning(*args, old_value, value, timestamp, **kwargs): + nonlocal timestamp_ + result = False if (timestamp_== 0) else (value not in ["Running", 4]) + print(f"Old {old_value}\tNew: {value}\tResult: {result}") + timestamp_ = timestamp + return result + + # Subscribe and wait for update + status = SubscriptionStatus(self.status, notRunning, settle_time=0.5) + return status + + def kickoff(self) -> StatusBase: + status = StatusBase() + status.set_finished() + return status + + def describe_collect(self) -> OrderedDict: + dd = OrderedDict() + dd['success'] = {'source': "internal", 'dtype': 'integer', 'shape': [], 'units': '', 'lower_ctrl_limit': 0, 'upper_ctrl_limit': 0} + return dd + + def collect(self) -> OrderedDict: + ret = OrderedDict() + ret["timestamps"] = {"success": time.time()} + ret["data"] = {"success": 1} + yield ret + + +class aa1DataAcquisition(Device): + """ Controller Data Acquisition + + This class implements the controller data collection feature of the + Automation1 controller. This feature logs various inputs at a + **fixed frequency** from 1 kHz up to 200 kHz. + Usage: + 1. Start a new configuration with "startConfig" + 2. Add your signals with "addXxxSignal" + 3. Start your data collection + 4. Read back the recorded data with "readback" + """ + # Status monitoring + status = Component(EpicsSignalRO, "RUNNING", auto_monitor=True, kind=Kind.hinted) + points_max = Component(EpicsSignal, "MAXPOINTS", kind=Kind.config, put_complete=True) + signal_num = Component(EpicsSignalRO, "NITEMS", kind=Kind.config) + + points_total = Component(EpicsSignalRO, "NTOTAL", auto_monitor=True, kind=Kind.hinted) + points_collected = Component(EpicsSignalRO, "NCOLLECTED", auto_monitor=True, kind=Kind.hinted) + points_retrieved= Component(EpicsSignalRO, "NRETRIEVED", auto_monitor=True, kind=Kind.hinted) + overflow = Component(EpicsSignalRO, "OVERFLOW", auto_monitor=True, kind=Kind.hinted) + runmode = Component(EpicsSignalRO, "MODE_RBV", auto_monitor=True, kind=Kind.hinted) + # DAQ setup + numpoints = Component(EpicsSignal, "NPOINTS", kind=Kind.config, put_complete=True) + frequency = Component(EpicsSignal, "FREQUENCY", kind=Kind.config, put_complete=True) + _configure = Component(EpicsSignal, "CONFIGURE", kind=Kind.omitted, put_complete=True) + + def startConfig(self, npoints: int, frequency: DataCollectionFrequency): + self.numpoints.set(npoints).wait() + self.frequency.set(frequency).wait() + self._configure.set("START").wait() + + def clearConfig(self): + self._configure.set("CLEAR").wait() + + srcTask = Component(EpicsSignal, "SRC_TASK", kind=Kind.config, put_complete=True) + srcAxis = Component(EpicsSignal, "SRC_AXIS", kind=Kind.config, put_complete=True) + srcCode = Component(EpicsSignal, "SRC_CODE", kind=Kind.config, put_complete=True) + _srcAdd = Component(EpicsSignal, "SRC_ADD", kind=Kind.omitted, put_complete=True) + + def addAxisSignal(self, axis: int, code: int) -> None: + """ Add a new axis-specific data signal to the DAQ configuration. The + most common signals are PositionFeedback and PositionError. + """ + self.srcAxis.set(axis).wait() + self.srcCode.set(code).wait() + self._srcAdd.set("AXIS").wait() + + def addTaskSignal(self, task: int, code: int) -> None: + """ Add a new task-specific data signal to the DAQ configuration""" + self.srcTask.set(task).wait() + self.srcCode.set(code).wait() + self._srcAdd.set("TASK").wait() + + def addSystemSignal(self, code: int) -> None: + """ Add a new system data signal to the DAQ configuration. The most + common signal is SampleCollectionTime. """ + self.srcCode.set(code).wait() + self._srcAdd.set("SYSTEM").wait() + + # Starting / stopping the DAQ + _mode = Component(EpicsSignal, "MODE", kind=Kind.config, put_complete=True) + _switch = Component(EpicsSignal, "SET", kind=Kind.omitted, put_complete=True) + + def start(self, mode=DataCollectionMode.Snapshot) -> None: + """ Start a new data collection """ + self._mode.set(mode).wait() + self._switch.set("START").wait() + + def stop(self) -> None: + """ Stop a running data collection """ + self._switch.set("STOP").wait() + + def run(self, mode=DataCollectionMode.Snapshot) -> None: + """ Start a new data collection """ + self._mode.set(mode).wait() + self._switch.set("START").wait() + # Wait for finishing acquisition + # Note: this is very bad blocking sleep + while self.status.value!=0: + sleep(0.1) + sleep(0.1) + + # Data readback + data = self.data_rb.get() + rows = self.data_rows.get() + cols = self.data_cols.get() + if len(data)==0 or rows==0 or cols==0: + sleep(0.5) + data = self.data_rb.get() + rows = self.data_rows.get() + cols = self.data_cols.get() + print(f"Data shape: {rows} x {cols}") + data = data.reshape([int(rows), -1]) + return data + + # DAQ data readback + data_rb = Component(EpicsSignalPassive, "DATA", kind=Kind.hinted) + data_rows = Component(EpicsSignalRO, "DATA_ROWS", auto_monitor=True, kind=Kind.hinted) + data_cols = Component(EpicsSignalRO, "DATA_COLS", auto_monitor=True, kind=Kind.hinted) + data_stat = Component(EpicsSignalRO, "DATA_AVG", auto_monitor=True, kind=Kind.hinted) + + def dataReadBack(self) -> np.ndarray: + """Retrieves collected data from the controller""" + data = self.data_rb.get() + rows = self.data_rows.get() + cols = self.data_cols.get() + if len(data)==0 or rows==0 or cols==0: + sleep(0.2) + data = self.data_rb.get() + rows = self.data_rows.get() + cols = self.data_cols.get() + print(f"Data shape: {rows} x {cols}") + data = data.reshape([int(rows), -1]) + return data + + + +class aa1GlobalVariables(Device): + """ Global variables + + This class provides an interface to directly read/write global variables + on the Automation1 controller. These variables are accesible from script + files and are thus a convenient way to interface with the outside word. + + Read operations take as input the memory address and the size + Write operations work with the memory address and value + """ + # Status monitoring + num_real = Component(EpicsSignalRO, "NUM-REAL_RBV", kind=Kind.config) + num_int = Component(EpicsSignalRO, "NUM-INT_RBV", kind=Kind.config) + num_string = Component(EpicsSignalRO, "NUM-STRING_RBV", kind=Kind.config) + + integer_addr = Component(EpicsSignal, "INT-ADDR", kind=Kind.omitted, put_complete=True) + integer_size = Component(EpicsSignal, "INT-SIZE", kind=Kind.omitted, put_complete=True) + integer = Component(EpicsSignal, "INT", kind=Kind.omitted, put_complete=True) + integer_rb = Component(EpicsSignalPassive, "INT-RBV", kind=Kind.omitted) + integerarr = Component(EpicsSignal, "INTARR", kind=Kind.omitted, put_complete=True) + integerarr_rb = Component(EpicsSignalPassive, "INTARR-RBV", kind=Kind.omitted) + + real_addr = Component(EpicsSignal, "REAL-ADDR", kind=Kind.omitted, put_complete=True) + real_size = Component(EpicsSignal, "REAL-SIZE", kind=Kind.omitted, put_complete=True) + real = Component(EpicsSignal, "REAL", kind=Kind.omitted, put_complete=True) + real_rb = Component(EpicsSignalPassive, "REAL-RBV", kind=Kind.omitted) + realarr = Component(EpicsSignal, "REALARR", kind=Kind.omitted, put_complete=True) + realarr_rb = Component(EpicsSignalPassive, "REALARR-RBV", kind=Kind.omitted) + + string_addr = Component(EpicsSignal, "STRING-ADDR", kind=Kind.omitted, put_complete=True) + string = Component(EpicsSignal, "STRING", kind=Kind.omitted, put_complete=True) + string_rb = Component(EpicsStringPassiveRO, "STRING-RBV", kind=Kind.omitted) + + def readInt(self, address: int, size: int=None) -> int: + """ Read a 64-bit integer global variable """ + if address > self.num_int.get(): + raise RuntimeError("Integer address {address} is out of range") + + if size is None: + self.integer_addr.set(address).wait() + return self.integer_rb.get() + else: + self.integer_addr.set(address).wait() + self.integer_size.set(size).wait() + return self.integerarr_rb.get() + + def writeInt(self, address: int, value) -> None: + """ Write a 64-bit integer global variable """ + if address > self.num_int.get(): + raise RuntimeError("Integer address {address} is out of range") + + if isinstance(value, (int, float)): + self.integer_addr.set(address).wait() + self.integer.set(value).wait() + elif isinstance(value, np.ndarray): + self.integer_addr.set(address).wait() + self.integerarr.set(value).wait() + elif isinstance(value, (list, tuple)): + value = np.array(value, dtype=np.int32) + self.integer_addr.set(address).wait() + self.integerarr.set(value).wait() + else: + raise RuntimeError("Unsupported integer value type: {type(value)}") + + + def readFloat(self, address: int, size: int=None) -> float: + """ Read a 64-bit double global variable """ + if address > self.num_real.get(): + raise RuntimeError("Floating point address {address} is out of range") + + if size is None: + self.real_addr.set(address).wait() + return self.real_rb.get() + else: + self.real_addr.set(address).wait() + self.real_size.set(size).wait() + return self.realarr_rb.get() + + def writeFloat(self, address: int, value) -> None: + """ Write a 64-bit float global variable """ + if address > self.num_real.get(): + raise RuntimeError("Float address {address} is out of range") + + if isinstance(value, (int, float)): + self.real_addr.set(address).wait() + self.real.set(value).wait() + elif isinstance(value, np.ndarray): + self.real_addr.set(address).wait() + self.realarr.set(value).wait() + elif isinstance(value, (list, tuple)): + value = np.array(value) + self.real_addr.set(address).wait() + self.realarr.set(value).wait() + else: + raise RuntimeError("Unsupported float value type: {type(value)}") + + def readString(self, address: int) -> str: + """ Read a 40 letter string global variable + ToDo: Automation 1 strings are 256 bytes + """ + if address > self.num_string.get(): + raise RuntimeError("String address {address} is out of range") + + self.string_addr.set(address).wait() + return self.string_rb.get() + + def writeString(self, address: int, value) -> None: + """ Write a 40 bytes string global variable """ + if address > self.num_string.get(): + raise RuntimeError("Integer address {address} is out of range") + + if isinstance(value, str): + self.string_addr.set(address).wait() + self.string.set(value).wait() + else: + raise RuntimeError("Unsupported string value type: {type(value)}") + + + +class aa1GlobalVariableBindings(Device): + """ Global variables + + This class provides an interface to directly read/write global variables + on the Automation1 controller. These variables are accesible from script + files and are thus a convenient way to interface with the outside word. + + Read operations take as input the memory address and the size + Write operations work with the memory address and value + """ + 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) + 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) + int8 = Component(EpicsSignal, "INT8_RBV", 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) + float1 = Component(EpicsSignalRO, "REAL1_RBV", auto_monitor=True, name="float1", 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) + 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) + + _str0 = Component(EpicsStringRO, "STR0_RBV", auto_monitor=True, name="str0_raw", kind=Kind.omitted) + str0 = Component(DerivedSignal, "_str0", inverse=epicsCharArray2String, auto_monitor=True, name="str0", kind=Kind.hinted) + + str1 = Component(EpicsStringRO, "STR1_RBV", auto_monitor=True, name="str1", kind=Kind.hinted) + str4 = Component(EpicsString, "STR4_RBV", put_complete=True, auto_monitor=True, write_pv="STR4", name="str4", kind=Kind.hinted) + str5 = Component(EpicsString, "STR5_RBV", put_complete=True, auto_monitor=True, write_pv="STR5", name="str4", kind=Kind.hinted) + + + +class aa1AxisIo(Device): + """ Analog / digital Input-Output + + This class provides convenience wrappers around the Aerotech API's axis + specific IO functionality. Note that this is a low-speed API, actual work + should be done in AeroScript. Only one IO pin is polled simultaneously! + """ + polllvl = Component(EpicsSignal, "POLLLVL", put_complete=True, kind=Kind.config) + + ai_addr = Component(EpicsSignal, "AI-ADDR", put_complete=True, kind=Kind.config) + ai = Component(EpicsSignalRO, "AI-RBV", auto_monitor=True, kind=Kind.hinted) + + ao_addr = Component(EpicsSignal, "AO-ADDR", put_complete=True, kind=Kind.config) + ao = Component(EpicsSignal, "AO-RBV", write_pv="AO", auto_monitor=True, kind=Kind.hinted) + + di_addr = Component(EpicsSignal, "DI-ADDR", put_complete=True, kind=Kind.config) + di = Component(EpicsSignalRO, "DI-RBV", auto_monitor=True, kind=Kind.hinted) + + do_addr = Component(EpicsSignal, "DO-ADDR", put_complete=True, kind=Kind.config) + do = Component(EpicsSignal, "DO-RBV", write_pv="DO", auto_monitor=True, kind=Kind.hinted) + + def setAnalog(self, pin: int, value: float, settle_time=0.05): + # Set the address + self.ao_addr.set(pin).wait() + # Set the voltage + self.ao.set(value, settle_time=settle_time).wait() + + def setDigital(self, pin: int, value: int, settle_time=0.05): + # Set the address + self.do_addr.set(pin).wait() + # Set the voltage + self.do.set(value, settle_time=settle_time).wait() + + + +class aa1AxisPsoBase(Device): + """ Position Sensitive Output - Base class + + This class provides convenience wrappers around the Aerotech IOC's PSO + functionality. As a base class, it's just a collection of PVs without + significant logic (that should be implemented in the child classes). + It uses event-waveform concept to produce signals on the configured + output pin: a specified position based event will trigger the generation + of a waveform on the oputput that can be either used as exposure enable, + as individual trigger or as a series of triggers per each event. + As a first approach, the module follows a simple pipeline structure: + Genrator --> Event --> Waveform --> Output + + Specific operation modes should be implemented in child classes. + """ + # ######################################################################## + # General module status + status = Component(EpicsSignalRO, "STATUS", auto_monitor=True, kind=Kind.hinted) + output = Component(EpicsSignalRO, "OUTPUT-RBV", auto_monitor=True, kind=Kind.hinted) + _eventSingle = Component(EpicsSignal, "EVENT:SINGLE", put_complete=True, kind=Kind.omitted) + _reset = Component(EpicsSignal, "RESET", put_complete=True, kind=Kind.omitted) + posInput = Component(EpicsSignal, "DIST:INPUT", put_complete=True, kind=Kind.omitted) + + # ######################################################################## + # PSO Distance event module + dstEventsEna = Component(EpicsSignal, "DIST:EVENTS", 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) + dstArrayDepleted = Component(EpicsSignalRO, "DIST:ARRAY-DEPLETED-RBV", auto_monitor=True, kind=Kind.hinted) + + dstDirection = Component(EpicsSignal, "DIST:EVENTDIR", put_complete=True, kind=Kind.omitted) + dstDistance = Component(EpicsSignal, "DIST:DISTANCE", put_complete=True, kind=Kind.hinted) + dstDistanceArr = Component(EpicsSignal, "DIST:DISTANCES", put_complete=True, kind=Kind.omitted) + dstArrayRearm = Component(EpicsSignal, "DIST:REARM-ARRAY", put_complete=True, kind=Kind.omitted) + + # ######################################################################## + # PSO Window event module + winEvents = Component(EpicsSignal, "WINDOW:EVENTS", put_complete=True, kind=Kind.omitted) + winOutput = Component(EpicsSignal, "WINDOW0:OUTPUT", put_complete=True, kind=Kind.omitted) + winInput = Component(EpicsSignal, "WINDOW0:INPUT", put_complete=True, kind=Kind.omitted) + winCounter = Component(EpicsSignal, "WINDOW0:COUNTER", put_complete=True, kind=Kind.omitted) + _winLower = Component(EpicsSignal, "WINDOW0:LOWER", put_complete=True, kind=Kind.omitted) + _winUpper = Component(EpicsSignal, "WINDOW0:UPPER", put_complete=True, kind=Kind.omitted) + + # ######################################################################## + # PSO waveform module + waveEnable = Component(EpicsSignal, "WAVE:ENABLE", put_complete=True, kind=Kind.omitted) + waveMode = Component(EpicsSignal, "WAVE:MODE", put_complete=True, kind=Kind.omitted) + #waveDelay = Component(EpicsSignal, "WAVE:DELAY", put_complete=True, kind=Kind.omitted) + + # PSO waveform pulse output + #pulseTrunc = Component(EpicsSignal, "WAVE:PULSE:TRUNC", put_complete=True, kind=Kind.omitted) + pulseOnTime = Component(EpicsSignal, "WAVE:PULSE:ONTIME", put_complete=True, kind=Kind.omitted) + pulseWindow = Component(EpicsSignal, "WAVE:PULSE:PERIOD", put_complete=True, kind=Kind.omitted) + pulseCount = Component(EpicsSignal, "WAVE:PULSE:COUNT", put_complete=True, kind=Kind.omitted) + pulseApply = Component(EpicsSignal, "WAVE:PULSE:APPLY", put_complete=True, kind=Kind.omitted) + + # ######################################################################## + # PSO output module + outPin = Component(EpicsSignal, "PIN", put_complete=True, kind=Kind.omitted) + outSource = Component(EpicsSignal, "SOURCE", put_complete=True, kind=Kind.omitted) + + def fire(self, settle_time=None): + """ Fire a single PSO event (i.e. manual software trigger)""" + self._eventSingle.set(1, settle_time=settle_time).wait() + + def toggle(self): + orig_waveMode = self.waveMode.get() + self.waveMode.set("Toggle").wait() + self.fire(0.1) + self.waveMode.set(orig_waveMode).wait() + + + + + +class aa1AxisPsoDistance(aa1AxisPsoBase): + """ Position Sensitive Output - Distance mode + + This class provides convenience wrappers around the Aerotech API's PSO + functionality in distance mode. It uses event-waveform concept to produce + signals on the configured output pin: a specified position based event + will trigger the generation af a waveform on the oputput that can be either + used as exposure enable, as individual trigger or as a series of triggers + per each event. + As a first approach, the module follows a simple pipeline structure: + Genrator --> Event --> Waveform --> Output + + The module provides configuration interface to common functionality, such + as fixed distance or array based triggering and can serve as a base for + future advanced functionality. The relative distances ease the limitations + coming from 32 bit PSO positions. + For a more detailed description of additional signals and masking plase + refer to Automation1's online manual. + """ + + def __init__(self, *args, **kwargs): + """ Member declarations in init""" + self._Vdistance = 3.141592 + super().__init__(*args, **kwargs) + + # ######################################################################## + # PSO high level interface + def configure(self, distance: Union[float, np.ndarray, list, tuple], wmode: str, + t_pulse: float=2000, w_pulse: float=5000, n_pulse: int=1, + posInput: int=None, pinOutput: int=None, **argv) -> None: + """ Simplified configuration interface to access the most common + functionality for distance mode PSO. + + :param distance: The trigger distance or the array of distances between subsequent points. + :param wmode: Waveform mode configuration, usually pulsed/toggled. + """ + # Validate input parameters + if wmode not in ["pulse", "pulsed", "toggle", "toggled"]: + raise RuntimeError(f"Unsupported distace triggering mode: {wmode}") + + + # Set the position data source and output pin + if posInput is not None: + self.posInput.set(posInput).wait() + if pinOutput is not None: + self.pinOutput.set(pinOutput).wait() + + # Configure distance generator (also resets counter to 0) + self._distanceValue = distance + if isinstance(distance, (float, int)): + self.dstDistance.set(distance).wait() + elif isinstance(distance, (np.ndarray, list, tuple)): + self.dstDistanceArr.set(distance).wait() + + self.winEvents.set("Off").wait() + self.dstCounterEna.set("Off").wait() + self.dstEventsEna.set("Off").wait() + + # Configure the pulsed/toggled waveform + if wmode in ["toggle", "toggled"]: + # Switching to simple toggle mode + self.waveEnable.set("On").wait() + self.waveMode.set("Toggle").wait() + + elif wmode in ["pulse", "pulsed"]: + # Switching to pulsed mode + self.waveEnable.set("On").wait() + self.waveMode.set("Pulse").wait() + # Setting pulse shape + self.pulseWindow.set(w_pulse).wait() + self.pulseOnTime.set(t_pulse).wait() + self.pulseCount.set(n_pulse).wait() + # Commiting configuration + self.pulseApply.set(1).wait() + # Enabling PSO waveform outputs + self.waveEnable.set("On").wait() + else: + raise RuntimeError(f"Unsupported waveform mode: {wmode}") + + # Ensure output is set to low + if self.output.value: + self.toggle() + + # Set PSO output data source + self.outSource.set("Waveform").wait() + + # ######################################################################## + # Bluesky step scan interface + def stage(self, settle_time=None): + self.dstEventsEna.set("On").wait() + if isinstance(self._distanceValue, (np.ndarray, list, tuple)): + self.dstArrayRearm.set(1).wait() + self.dstCounterEna.set("On").wait() + if settle_time is not None: + sleep(settle_time) + return super().stage() + + def trigger(self): + return super().trigger() + + def unstage(self): + # Turn off counter monitoring + self.dstEventsEna.set("Off").wait() + self.dstCounterEna.set("Off").wait() + return super().unstage() + # ######################################################################## + # Bluesky flyer interface + def kickoff(self) -> StatusBase: + # Rearm the configured array + if hasattr(self, "_distanceValue") and isinstance(self._distanceValue, (np.ndarray, list, tuple)): + self.dstArrayRearm.set(1).wait() + # Start monitoring the counters + self.dstEventsEna.set("On").wait() + self.dstCounterEna.set("On").wait() + status = StatusBase() + status.set_finished() + return status + + def complete(self) -> StatusBase: + """ Bluesky flyer interface""" + # Array mode waits until the buffer is empty + if isinstance(self._distanceValue, (np.ndarray, list, tuple)): + # Define wait until the busy flag goes down (excluding initial update) + timestamp_ = 0 + def notRunning(*args, old_value, value, timestamp, **kwargs): + nonlocal timestamp_ + result = False if (timestamp_== 0) else bool(int(value) & 0x1000) + print(f"Old {old_value}\tNew: {value}\tResult: {result}") + timestamp_ = timestamp + return result + + # Subscribe and wait for update + status = SubscriptionStatus(self.status, notRunning, settle_time=0.5) + else: + # In distance trigger mode there's no specific goal + status = StatusBase() + status.set_finished() + return status + + def describe_collect(self) -> OrderedDict: + ret = OrderedDict() + ret['index'] = {'source': "internal", 'dtype': 'integer', 'shape': [], 'units': '', 'lower_ctrl_limit': 0, 'upper_ctrl_limit': 0} + return {self.name: ret} + + def collect(self) -> OrderedDict: + ret = OrderedDict() + ret["timestamps"] = {"index": time.time()} + ret["data"] = {"index": self.dstCounterVal.value } + yield ret + + + + +class aa1AxisPsoWindow(aa1AxisPsoBase): + """ Position Sensitive Output - Window mode + + This class provides convenience wrappers around the Aerotech API's PSO + functionality in window mode. It can either use the event-waveform concept + or provide a direct window output signal (in/out) to the output pin. The + latter is particularly well-suited for the generation of trigger enable + signals, while in event mode it allows the finetuning of trigger lenth. + As a first approach, the module follows a simple pipeline structure: + Genrator --> Event --> Waveform --> Output pin + Genrator --> Window output --> Output pin + + The module provides configuration interface to common functionality, such + as repeated trigger enable signal or fixed area scaning. Unfortunately the + entered positions are absolute, meaning this mode has an inherent limitation + with encoder counters being kept in 32 bit integers. + For a more detailed description of additional signals and masking plase + refer to Automation1's online manual. + """ + def __init__(self, *args, **kwargs): + """ Member declarations in init""" + self._mode = "output" + self._eventMode = "Enter" + super().__init__(*args, **kwargs) + + # ######################################################################## + # PSO high level interface + def configure(self, bounds: Union[np.ndarray, list, tuple], wmode: str, emode: str="Enter", + t_pulse: float=2000, w_pulse: float=5000, n_pulse: int=1, + posInput: int=None, pinOutput: int=None, **argv) -> None: + """ Simplified configuration interface to access the most common + functionality for distance mode PSO. + + :param distance: The trigger distance or the array of distances between subsequent points. + :param wmode: Waveform mode configuration, usually output/pulsed/toggled. + """ + # Validate input parameters + if wmode not in ["pulse", "pulsed", "toggle", "toggled", "output", "flag"]: + raise RuntimeError(f"Unsupported window triggering mode: {wmode}") + self._mode = wmode + self._eventMode = emode + + # Set the position data source and output pin + if posInput is not None: + self.posInput.set(posInput).wait() + if pinOutput is not None: + self.outPin.set(pinOutput).wait() + + # Configure the window module + # Set the window ranges (MUST be in start position) + if len(bounds) == 2: + self.winCounter.set(0).wait() + self._winLower.set(bounds[0]).wait() + self._winUpper.set(bounds[1]).wait() + + elif isinstance(bounds, np.ndarray): + # ToDo... + pass + + # Don't start triggering just yet + self.winOutput.set("Off").wait() + self.winEvents.set("Off").wait() + + # Configure the pulsed/toggled waveform + if wmode in ["toggle", "toggled"]: + # Switching to simple toggle mode + self.waveEnable.set("On").wait() + self.waveMode.set("Toggle").wait() + elif wmode in ["pulse", "pulsed"]: + # Switching to pulsed mode + self.waveEnable.set("On").wait() + self.waveMode.set("Pulse").wait() + # Setting pulse shape + self.pulseWindow.set(w_pulse).wait() + self.pulseOnTime.set(t_pulse).wait() + self.pulseCount.set(n_pulse).wait() + # Commiting configuration + self.pulseApply.set(1).wait() + # Enabling PSO waveform outputs + self.waveEnable.set("On").wait() + elif wmode in ["output", "flag"]: + self.waveEnable.set("Off").wait() + else: + raise RuntimeError(f"Unsupported window mode: {wmode}") + + # Set PSO output data source + if wmode in ["toggle", "toggled", "pulse", "pulsed"]: + self.outSource.set("Waveform").wait() + elif wmode in ["output", "flag"]: + self.outSource.set("Window").wait() + + def stage(self, settle_time=None): + if self.outSource.get() in ["Window", 2]: + self.winOutput.set("On").wait() + else: + self.winEvents.set(self._eventMode).wait() + if settle_time is not None: + sleep(settle_time) + return super().stage() + + def kickoff(self, settle_time=None): + if self.outSource.get() in ["Window", 2]: + self.winOutput.set("On").wait() + else: + self.winEvents.set(self._eventMode).wait() + if settle_time is not None: + sleep(settle_time) + + def unstage(self, settle_time=None): + self.winOutput.set("Off").wait() + self.winEvents.set("Off").wait() + if settle_time is not None: + sleep(settle_time) + return super().unstage() + + + + + + + +class aa1AxisDriveDataCollection(Device): + """ Axis data collection + + This class provides convenience wrappers around the Aerotech API's axis + specific data collection functionality. This module allows to record + hardware synchronized signals with up to 200 kHz. + + The default configuration is using a fixed memory mapping allowing up to + 1 million recorded data points on an XC4e (this depends on controller). + """ + + # ######################################################################## + # General module status + nsamples_rbv = Component(EpicsSignalRO, "SAMPLES_RBV", auto_monitor=True, kind=Kind.hinted) + _switch = Component(EpicsSignal, "ACQUIRE", put_complete=True, kind=Kind.omitted) + _input0 = Component(EpicsSignal, "INPUT0", put_complete=True, kind=Kind.config) + _input1 = Component(EpicsSignal, "INPUT1", put_complete=True, kind=Kind.config) + _trigger = Component(EpicsSignal, "TRIGGER", put_complete=True, kind=Kind.config) + + npoints = Component(EpicsSignal, "NPOINTS", put_complete=True, kind=Kind.config) + _readback0 = Component(EpicsSignal, "AREAD0", kind=Kind.omitted) + _readstatus0 = Component(EpicsSignalRO, "AREAD0_RBV", auto_monitor=True, kind=Kind.omitted) + _readback1 = Component(EpicsSignal, "AREAD1", kind=Kind.omitted) + _readstatus1 = Component(EpicsSignalRO, "AREAD1_RBV", auto_monitor=True, kind=Kind.omitted) + + _buffer0 = Component(EpicsSignalRO, "BUFFER0", auto_monitor=True, kind=Kind.hinted) + _buffer1 = Component(EpicsSignalRO, "BUFFER1", auto_monitor=True, kind=Kind.hinted) + + def configure(self, npoints, + trigger: int=DriveDataCaptureTrigger.PsoOutput, + source0: int=DriveDataCaptureInput.PrimaryFeedback, + source1: int=DriveDataCaptureInput.PositionCommand): + self._input0.set(source0).wait() + self._input1.set(source1).wait() + self._trigger.set(trigger).wait() + # This allocates the memory... + self.npoints.set(npoints).wait() + + + # Bluesky step scanning interface + def stage(self, settle_time=0.1): + super().stage() + self._switch.set("Start", settle_time=0.5).wait() + status = Status(timeout=0.1, settle_time=settle_time).set_finished() + return status + + def unstage(self, settle_time=0.1): + self._switch.set("Stop", settle_time=settle_time).wait() + super().unstage() + + # Bluesky flyer interface + def kickoff(self, settle_time=0.1) -> Status: + status = self._switch.set("Start", settle_time=settle_time) + return status + + def complete(self, settle_time=0.1) -> Status: + """ DDC just reads back whatever is available in the buffers""" + status = Status(settle_time=settle_time) + status.set_finished() + return status + + def _collect(self, index=0): + """ Force a readback of the data buffer + + Note that there's a weird behaviour in ophyd that it issues an + initial update event with the initial value but 0 timestamp. Theese + old_values are invalid and must be filtered out. + """ + + # Define wait until the busy flag goes down (excluding initial update) + timestamp_ = 0 + def negEdge(*args, old_value, value, timestamp, **kwargs): + nonlocal timestamp_ + result = False if (timestamp_== 0) else (old_value == 1 and value == 0) + # print(f"\nBuffer1 status:\t{old_value} ({timestamp_}) to {value} ({timestamp}) Result: {result}") + timestamp_ = timestamp + return result + + if index==0: + status = SubscriptionStatus(self._readstatus0, negEdge, settle_time=0.5) + self._readback0.set(1).wait() + elif index==1: + status = SubscriptionStatus(self._readstatus1, negEdge, settle_time=0.5) + self._readback1.set(1).wait() + + # Start asynchronous readback + status.wait() + return status + + + def describe_collect(self) -> OrderedDict: + ret = OrderedDict() + ret['buffer0'] = {'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} + + def collect(self) -> OrderedDict: + + self._collect(0).wait() + self._collect(1).wait() + + b0 = self._buffer0.value + b1 = self._buffer1.value + ret = OrderedDict() + ret["timestamps"] = {"buffer0": time.time(), "buffer1": time.time() } + ret["data"] = {"buffer0": b0, "buffer1": b1 } + yield ret + + + + +# Automatically start simulation if directly invoked +if __name__ == "__main__": + + # Drive data collection + tcDdc = aa1AxisDriveDataCollection("X02DA-ES1-SMP1:ROTY:DDC:", name="tcddc") + tcDdc.wait_for_connection() + + diff --git a/ophyd_devices/epics/devices/AerotechAutomation1Enums.py b/ophyd_devices/epics/devices/AerotechAutomation1Enums.py new file mode 100644 index 0000000..ac0f981 --- /dev/null +++ b/ophyd_devices/epics/devices/AerotechAutomation1Enums.py @@ -0,0 +1,914 @@ +from enum import Enum + + +class TomcatSequencerState: + IDLE = 0 + READY = 1 + ARMED = 2 + RUNNING = 3 + CHANGING = 4 + ERROR = 15 + + +class AxisDataSignal: + PositionFeedback = 0 + PositionCommand = 1 + PositionError = 2 + VelocityFeedback = 3 + VelocityCommand = 4 + VelocityError = 5 + AccelerationCommand = 6 + CurrentError = 9 + PositionCommandRaw = 12 + VelocityCommandRaw = 13 + AuxiliaryFeedback = 14 + DigitalInput = 15 + DigitalOutput = 16 + FixtureOffset = 18 + CoordinatedPositionTarget = 41 + DriveStatus = 42 + AxisStatus = 43 + AxisFault = 44 + AccelerationCommandRaw = 45 + PositionCalibrationAll = 50 + PositionFeedbackRollover = 63 + PositionCommandRollover = 64 + VelocityFeedbackAverage = 65 + CurrentFeedbackAverage = 66 + AxisParameter = 68 + Backlash = 72 + HomeState = 73 + PositionCalibration2D = 74 + NormalcyDebug = 75 + TotalMoveTime = 76 + JerkCommandRaw = 78 + ProgramPositionCommand = 79 + PositionOffset = 80 + PositionCommandRawBackwardsDiff = 82 + VelocityCommandRawBackwardsDiffDelta = 83 + DriveStatusActual = 85 + ProgramPositionFeedback = 89 + JogTrajectoryStatus = 94 + PingTest = 95 + AccelerationTime = 109 + DecelerationTime = 110 + AccelerationRate = 111 + DecelerationRate = 112 + AccelerationType = 113 + DecelerationType = 114 + AccelerationMode = 115 + DecelerationMode = 116 + ProgramPosition = 124 + SpeedTarget = 128 + PositionCommandPacket = 131 + DriveSmcMotionState = 132 + PositionCommandRawCal = 140 + VelocityCommandRawCal = 141 + VelocityCommandDrive = 142 + AccelerationCommandDrive = 143 + GalvoLaserOutputRaw = 144 + DriveInterfacePacketInt32 = 147 + DriveInterfacePacketInt16 = 148 + DriveInterfacePacketInt8 = 149 + DriveInterfacePacketDouble = 150 + DriveInterfacePacketFloat = 151 + DriveInterfaceCommandCode = 152 + AccelerationFeedback = 153 + AccelerationCommandRawCal = 154 + PositionCalibrationAllDrive = 155 + BacklashTarget = 156 + DriveMotionRate = 158 + DriveMotionDelay = 159 + CalibrationAdjustmentValue = 160 + ServoRounding = 161 + FeedforwardCurrent = 162 + DriveInterfacePacketInfoBitValue = 164 + AccelerationError = 165 + SuppressedFaults = 167 + DriveInterfacePacketStreamingData = 168 + PositionCommandRawUnfiltered = 169 + TransitionOffsetErrors = 170 + FreezeVelocityCommand = 179 + FreezeVelocityFeedback = 180 + InternalPositionOffsets = 181 + StatusHighLevelOffsetsLastMsec = 182 + ProgramVelocityCommand = 183 + ProgramVelocityFeedback = 184 + DriveMotionDelayLive = 185 + DriveCommunicationDelay = 186 + DriveCommunicationDelayLive = 187 + DriveInterfacePacketResponseInt32 = 189 + DriveInterfacePacketResponseInt16 = 190 + DriveInterfacePacketResponseInt8 = 191 + DriveInterfacePacketResponseDouble = 192 + DriveInterfacePacketResponseFloat = 193 + DriveInterfacePacketBit = 194 + DriveInterfacePacketResponseBit = 195 + SpeedTargetActual = 196 + CoordinatedDistanceRemaining = 199 + SafeZoneState = 230 + PositionErrorGalvo = 235 + MoveReferencePosition = 237 + MoveReferenceCutterOffset = 250 + MoveReferenceCornerOffset = 251 + MoveReferenceTotalOffset = 252 + DistanceLog = 264 + AutoFocusError = 295 + GalvoLaserOutputRawAdvance = 296 + GalvoLaserOnDelay = 297 + GalvoLaserOffDelay = 298 + CalibrationAdjustmentState = 301 + AccuracyCorrectionStartingPosition = 302 + AccuracyCorrectionEndingPosition = 303 + DriveCommandsDelayed = 309 + DriveCommandsLost = 310 + StoStatus = 327 + DriveAssert = 336 + PrimaryFeedback = 366 + AccelerationSCurvePercentage = 371 + DecelerationSCurvePercentage = 372 + GantryMarkerDifference = 390 + PrimaryFeedbackStatus = 392 + HomeTargetPosition = 398 + GantryRealignmentMoveTargetPosition = 399 + GantryDriveControlRealignmentState = 400 + DriveInterfacePositionCommandPhysical = 434 + DriveControlReason = 435 + CurrentFeedback = 7 + CurrentCommand = 8 + AnalogInput0 = 10 + AnalogInput1 = 11 + PhaseACurrentFeedback = 19 + PhaseBCurrentFeedback = 20 + EncoderSine = 21 + EncoderCosine = 22 + AnalogInput2 = 23 + AnalogInput3 = 24 + FrequencyResponseBefore = 25 + FrequencyResponseAfter = 26 + AnalogOutput0 = 31 + AnalogOutput1 = 32 + AnalogOutput2 = 33 + AnalogOutput3 = 34 + DriveMemoryInt32 = 35 + DriveMemoryFloat = 36 + DriveMemoryDouble = 37 + PsoStatus = 38 + DriveTimerDebug = 39 + PositionFeedbackDrive = 77 + PositionCommandDrive = 84 + DriveMemoryInt16 = 125 + DriveMemoryInt8 = 126 + PsoCounter0 = 171 + PsoCounter1 = 172 + PsoCounter2 = 173 + PsoWindow0 = 174 + PsoWindow1 = 175 + DriveDataCaptureSamples = 176 + PositionCommandGalvo = 178 + PrimaryEnDatAbsolutePosition = 197 + ControlEffort = 201 + PhaseAVoltageCommand = 208 + PhaseBVoltageCommand = 209 + PhaseCVoltageCommand = 210 + AmplifierPeakCurrent = 211 + FpgaVersion = 212 + DriveTypeId = 213 + PsoWindow0ArrayIndex = 214 + PsoWindow1ArrayIndex = 215 + PsoDistanceArrayIndex = 216 + AmplifierTemperature = 217 + PsoBitArrayIndex = 218 + MxAbsolutePosition = 219 + ServoUpdateRate = 220 + SettlingTime = 221 + InternalStatusCode = 222 + FirmwareVersionMajor = 223 + FirmwareVersionMinor = 224 + FirmwareVersionPatch = 225 + FirmwareVersionBuild = 226 + DriveTimerDebugMax = 227 + MarkerSearchDistance = 228 + PositionFeedbackGalvo = 234 + LatchedMarkerPosition = 236 + PrimaryBissAbsolutePosition = 255 + FaultPositionFeedback = 258 + MotorCommutationAngle = 259 + ExpansionBoardOption = 260 + BusVoltage = 261 + PiezoVoltageCommand = 262 + PiezoVoltageFeedback = 263 + TimeSinceReset = 273 + MaximumVoltage = 274 + CommandOutputType = 275 + DriveFeedforwardOutput = 290 + LastTickCounter = 291 + BoardRevision = 292 + GalvoLaserOutput = 294 + GalvoLaserPowerCorrectionOutput = 299 + CapacitanceSensorRawPosition = 300 + PositionCalibrationGalvo = 304 + BusVoltageNegative = 325 + ProcessorTemperature = 326 + InternalStatusTimestamp = 328 + AnalogSensorInput = 329 + MotorTemperature = 330 + PrimaryBissStatus = 332 + PsoExternalSyncFrequency = 337 + EncoderSineRaw = 346 + EncoderCosineRaw = 347 + FpgaTemperature = 353 + PrimaryEnDatStatus = 355 + DriveTimerHighPriorityThread = 356 + DriveTimerLowPriorityThread = 357 + DriveTimerLowPriorityPacket = 358 + DriveTimerServoPacket = 359 + DriveTimerServoThread = 360 + DriveTimerCurrentPacket = 361 + DriveTimerCommonCoreThread = 362 + DriveTimerServoCorePacket0 = 363 + DriveTimerServoCorePacket1 = 364 + MultiplierOption = 365 + ServoLoopFeedbackInput0 = 367 + ServoLoopFeedbackInput1 = 368 + FaultSubcode = 376 + ProcessorTemperatureMax = 378 + DriveTimerHyperWireDma = 381 + AmplifierTemperatureMax = 382 + AuxiliaryEnDatAbsolutePosition = 383 + AuxiliaryEnDatStatus = 384 + AuxiliaryBissAbsolutePosition = 385 + AuxiliaryBissStatus = 386 + PsoOption = 387 + DriveArraySize = 388 + RatedMotorSupplyVoltageOption = 389 + AbsoluteEncoderOption = 391 + AuxiliaryFeedbackStatus = 393 + AmplifierStatus = 394 + LatchedCwLimitPosition = 395 + LatchedCcwLimitPosition = 396 + GalvoLaserFpgaTransitionDelay = 397 + PiezoAccumulatedCharge = 401 + PiezoChargingTime = 402 + PrimarySsiAbsolutePosition = 403 + PrimarySsiStatus = 404 + AuxiliarySsiAbsolutePosition = 405 + AuxiliarySsiStatus = 406 + PsoDistanceActiveDistance = 407 + PsoWindow0ActiveLowerBound = 408 + PsoWindow0ActiveUpperBound = 409 + PsoWindow1ActiveLowerBound = 410 + PsoWindow1ActiveUpperBound = 411 + PsoWaveformActiveTotalTime = 412 + PsoWaveformActiveOnTime = 413 + PsoWaveformActivePulseCount = 414 + PsoEventActiveBitValue = 415 + DriveTimerDriveBasedControllerOutputDma = 419 + DriveTimerPcieInboundFsm = 420 + PrimaryFeedbackServo = 425 + AuxiliaryFeedbackServo = 426 + DriveStackUsage = 427 + ShuntResistorTemperature = 436 + + +class TaskDataSignal: + ProgramLineNumber = 17 + CoordinatedFlags = 40 + CoordinatedArcStartAngle = 53 + CoordinatedArcEndAngle = 54 + CoordinatedArcRadius = 55 + CoordinatedArcRadiusError = 56 + CoordinatedPositionCommand = 57 + CoordinatedSpeedCommand = 58 + CoordinatedAccelerationCommand = 59 + CoordinatedTotalDistance = 60 + CoordinatedPercentDone = 61 + CoordinatedPositionCommandBackwardsDiff = 62 + TaskParameter = 69 + TaskError = 70 + TaskWarning = 71 + CoordinatedSpeedTargetActual = 86 + DependentCoordinatedSpeedTargetActual = 87 + ActiveFixtureOffset = 88 + TaskStatus0 = 90 + TaskStatus1 = 91 + TaskStatus2 = 92 + SpindleSpeedTarget = 93 + CoordinateSystem1I = 96 + CoordinateSystem1J = 97 + CoordinateSystem1K = 98 + CoordinateSystem1Plane = 99 + ToolNumberActive = 100 + Mfo = 101 + CoordinatedSpeedTarget = 102 + DependentCoordinatedSpeedTarget = 103 + CoordinatedAccelerationRate = 104 + CoordinatedDecelerationRate = 105 + CoordinatedAccelerationTime = 106 + CoordinatedDecelerationTime = 107 + TaskMode = 108 + TaskState = 117 + TaskStateInternal = 118 + ExecutionMode = 121 + EnableAlignmentAxes = 127 + CoordinatedGalvoLaserOutput = 133 + CoordinatedMotionRate = 145 + CoordinatedTaskCommand = 146 + EnableState = 166 + LookaheadMovesExamined = 200 + ProfileControlMask = 231 + CoordinatedArcRadiusReciprocal = 253 + MotionEngineStage = 254 + CoordinatedTimeScale = 256 + CoordinatedTimeScaleDerivative = 257 + IfovSpeedScale = 266 + IfovSpeedScaleAverage = 267 + IfovGenerationFrameCounter = 268 + IfovGenerationTimeOriginal = 269 + IfovGenerationTimeModified = 270 + IfovCoordinatedPositionCommand = 271 + IfovCoordinatedSpeedCommand = 272 + IfovCenterPointH = 276 + IfovCenterPointV = 277 + IfovTrajectoryCount = 278 + IfovTrajectoryIndex = 279 + IfovAttemptCode = 280 + IfovGenerationFrameIndex = 281 + IfovMaximumVelocity = 282 + IfovIdealVelocity = 283 + TaskInternalDebug = 284 + IfovCoordinatedAccelerationCommand = 285 + IfovFovPositionH = 286 + IfovFovPositionV = 287 + IfovFovDimensionH = 288 + IfovFovDimensionV = 289 + MotionBufferElements = 311 + MotionBufferMoves = 312 + MotionLineNumber = 313 + MotionBufferRetraceMoves = 314 + MotionBufferRetraceElements = 315 + MotionBufferIndex = 316 + MotionBufferIndexLookahead = 317 + MotionBufferProcessingBlocked = 318 + ActiveMoveValid = 319 + TaskExecutionLines = 320 + SchedulerTaskHolds = 321 + SchedulerProgramLoopRuns = 322 + SchedulerTaskBlocked = 323 + CriticalSectionsActive = 324 + AxesSlowdownReason = 331 + TaskExecutionTime = 333 + TaskExecutionTimeMaximum = 334 + TaskExecutionLinesMaximum = 335 + LookaheadDecelReason = 338 + LookaheadDecelMoves = 339 + LookaheadDecelDistance = 340 + ProgramCounter = 341 + StackPointer = 342 + FramePointer = 343 + StringStackPointer = 344 + ProgramLineNumberSourceFileId = 349 + MotionLineNumberSourceFileId = 350 + ProgramLineNumberSourcePathId = 351 + MotionLineNumberSourcePathId = 352 + StringArgumentStackPointer = 354 + CoordinatedAccelerationSCurvePercentage = 369 + CoordinatedDecelerationSCurvePercentage = 370 + DependentCoordinatedAccelerationRate = 373 + DependentCoordinatedDecelerationRate = 374 + CriticalSectionTimeout = 375 + CommandQueueCapacity = 421 + CommandQueueUnexecutedCount = 422 + CommandQueueTimesEmptied = 423 + CommandQueueExecutedCount = 424 + + + + + +class SystemDataSignal: + VirtualBinaryInput = 46 + VirtualBinaryOutput = 47 + VirtualRegisterInput = 48 + VirtualRegisterOutput = 49 + Timer = 51 + TimerPerformance = 52 + GlobalReal = 67 + CommunicationRealTimeErrors = 81 + LibraryCommand = 119 + DataCollectionSampleTime = 120 + DataCollectionSampleIndex = 129 + ModbusClientConnected = 134 + ModbusServerConnected = 135 + ModbusClientError = 136 + ModbusServerError = 137 + StopWatchTimer = 157 + ScopetrigId = 163 + EstimatedProcessorUsage = 177 + DataCollectionStatus = 188 + SignalLogState = 198 + SafeZoneViolationMask = 207 + SafeZoneActiveMask = 229 + ModbusClientInputWords = 240 + ModbusClientOutputWords = 241 + ModbusClientInputBits = 242 + ModbusClientOutputBits = 243 + ModbusClientOutputBitsStatus = 244 + ModbusClientOutputWordsStatus = 245 + ModbusServerInputWords = 246 + ModbusServerOutputWords = 247 + ModbusServerInputBits = 248 + ModbusServerOutputBits = 249 + SystemParameter = 265 + ThermoCompSensorTemperature = 305 + ThermoCompControllingTemperature = 306 + ThermoCompCompensatingTemperature = 307 + ThermoCompStatus = 308 + GlobalInteger = 345 + AliveAxesMask = 348 + SignalLogPointsStored = 377 + ControllerInitializationWarning = 379 + StopWatchTimerMin = 416 + StopWatchTimerMax = 417 + StopWatchTimerAvg = 418 + EthercatEnabled = 428 + EthercatError = 429 + EthercatTxPdo = 430 + EthercatTxPdoSize = 431 + EthercatRxPdo = 432 + EthercatRxPdoSize = 433 + EthercatState = 437 + ModbusClientEnabled = 438 + ModbusServerEnabled = 439 + + + +class DataCollectionFrequency: + Undefined = 0 + Fixed1kHz = 1 + Fixed10kHz = 2 + Fixed20kHz = 3 + Fixed100kHz = 4 + Fixed200kHz = 5 + + +class DataCollectionMode: + Snapshot = 0 + Continouous = 1 + + +# Specifies the PSO distance input settings for the XC4e drive. +class PsoDistanceInput: + XC4PrimaryFeedback = 130 + XC4AuxiliaryFeedback = 131 + XC4SyncPortA = 132 + XC4SyncPortB = 133 + XC4DrivePulseStream = 134 + XC4ePrimaryFeedback = 135 + XC4eAuxiliaryFeedback = 136 + XC4eSyncPortA = 137 + XC4eSyncPortB = 138 + XC4eDrivePulseStream = 139 + + +class PsoWindowInput: + XC4PrimaryFeedback = 130 + XC4AuxiliaryFeedback = 131 + XC4SyncPortA = 132 + XC4SyncPortB = 133 + XC4DrivePulseStream = 134 + XC4ePrimaryFeedback = 135 + XC4eAuxiliaryFeedback = 136 + XC4eSyncPortA = 137 + XC4eSyncPortB = 138 + XC4eDrivePulseStream = 139 + XL5ePrimaryFeedback = 145, + XL5eAuxiliaryFeedback = 146, + XL5eSyncPortA = 147, + XL5eSyncPortB = 148, + XL5eDrivePulseStream = 149, + +# @brief Specifies the PSO output pin settings for each drive. +class XC4ePsoOutputPin: + DedicatedOutput = 111 + AuxiliaryMarkerDifferential = 112 + AuxiliaryMarkerSingleEnded = 113 + +class XC4PsoOutputPin: + DedicatedOutput = 108 + AuxiliaryMarkerDifferential = 109 + AuxiliaryMarkerSingleEnded = 110 + + +""" +# @brief Specifies the PSO distance input settings for each drive. +class Automation1PsoDistanceInput: + Automation1PsoDistanceInput_GL4PrimaryFeedbackAxis1 = 100, + Automation1PsoDistanceInput_GL4PrimaryFeedbackAxis2 = 101, + Automation1PsoDistanceInput_GL4IfovFeedbackAxis1 = 102, + Automation1PsoDistanceInput_GL4IfovFeedbackAxis2 = 103, + Automation1PsoDistanceInput_GL4AuxiliaryFeedbackAxis1 = 104, + Automation1PsoDistanceInput_GL4AuxiliaryFeedbackAxis2 = 105, + Automation1PsoDistanceInput_GL4SyncPortA = 106, + Automation1PsoDistanceInput_GL4SyncPortB = 107, + Automation1PsoDistanceInput_GL4DrivePulseStreamAxis1 = 108, + Automation1PsoDistanceInput_GL4DrivePulseStreamAxis2 = 109, + Automation1PsoDistanceInput_XL4sPrimaryFeedback = 110, + Automation1PsoDistanceInput_XL4sAuxiliaryFeedback = 111, + Automation1PsoDistanceInput_XL4sSyncPortA = 112, + Automation1PsoDistanceInput_XL4sSyncPortB = 113, + Automation1PsoDistanceInput_XL4sDrivePulseStream = 114, + Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis1 = 115, + Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis2 = 116, + Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis3 = 117, + Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis4 = 118, + Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis5 = 119, + Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis6 = 120, + Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis1 = 121, + Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis2 = 122, + Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis3 = 123, + Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis4 = 124, + Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis5 = 125, + Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis6 = 126, + Automation1PsoDistanceInput_XR3SyncPortA = 127, + Automation1PsoDistanceInput_XR3SyncPortB = 128, + Automation1PsoDistanceInput_XR3DrivePulseStream = 129, + Automation1PsoDistanceInput_XC4PrimaryFeedback = 130, + Automation1PsoDistanceInput_XC4AuxiliaryFeedback = 131, + Automation1PsoDistanceInput_XC4SyncPortA = 132, + Automation1PsoDistanceInput_XC4SyncPortB = 133, + Automation1PsoDistanceInput_XC4DrivePulseStream = 134, + XC4ePrimaryFeedback = 135, + XC4eAuxiliaryFeedback = 136, + XC4eSyncPortA = 137, + XC4eSyncPortB = 138, + XC4eDrivePulseStream = 139, + Automation1PsoDistanceInput_XC6ePrimaryFeedback = 140, + Automation1PsoDistanceInput_XC6eAuxiliaryFeedback = 141, + Automation1PsoDistanceInput_XC6eSyncPortA = 142, + Automation1PsoDistanceInput_XC6eSyncPortB = 143, + Automation1PsoDistanceInput_XC6eDrivePulseStream = 144, + Automation1PsoDistanceInput_XL5ePrimaryFeedback = 145, + Automation1PsoDistanceInput_XL5eAuxiliaryFeedback = 146, + Automation1PsoDistanceInput_XL5eSyncPortA = 147, + Automation1PsoDistanceInput_XL5eSyncPortB = 148, + Automation1PsoDistanceInput_XL5eDrivePulseStream = 149, + Automation1PsoDistanceInput_XC2PrimaryFeedback = 150, + Automation1PsoDistanceInput_XC2AuxiliaryFeedback = 151, + Automation1PsoDistanceInput_XC2DrivePulseStream = 152, + Automation1PsoDistanceInput_XC2ePrimaryFeedback = 153, + Automation1PsoDistanceInput_XC2eAuxiliaryFeedback = 154, + Automation1PsoDistanceInput_XC2eDrivePulseStream = 155, + Automation1PsoDistanceInput_XL2ePrimaryFeedback = 156, + Automation1PsoDistanceInput_XL2eAuxiliaryFeedback = 157, + Automation1PsoDistanceInput_XL2eSyncPortA = 158, + Automation1PsoDistanceInput_XL2eSyncPortB = 159, + Automation1PsoDistanceInput_XL2eDrivePulseStream = 160, + Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis1 = 161, + Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis2 = 162, + Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis3 = 163, + Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis4 = 164, + Automation1PsoDistanceInput_XI4AuxiliaryFeedback1 = 165, + Automation1PsoDistanceInput_XI4AuxiliaryFeedback2 = 166, + Automation1PsoDistanceInput_XI4AuxiliaryFeedback3 = 167, + Automation1PsoDistanceInput_XI4AuxiliaryFeedback4 = 168, + Automation1PsoDistanceInput_XI4SyncPortA = 169, + Automation1PsoDistanceInput_XI4SyncPortB = 170, + Automation1PsoDistanceInput_XI4DrivePulseStreamAxis1 = 171, + Automation1PsoDistanceInput_XI4DrivePulseStreamAxis2 = 172, + Automation1PsoDistanceInput_XI4DrivePulseStreamAxis3 = 173, + Automation1PsoDistanceInput_XI4DrivePulseStreamAxis4 = 174, + Automation1PsoDistanceInput_iXC4PrimaryFeedback = 175, + Automation1PsoDistanceInput_iXC4AuxiliaryFeedback = 176, + Automation1PsoDistanceInput_iXC4SyncPortA = 177, + Automation1PsoDistanceInput_iXC4SyncPortB = 178, + Automation1PsoDistanceInput_iXC4DrivePulseStream = 179, + Automation1PsoDistanceInput_iXC4ePrimaryFeedback = 180, + Automation1PsoDistanceInput_iXC4eAuxiliaryFeedback = 181, + Automation1PsoDistanceInput_iXC4eSyncPortA = 182, + Automation1PsoDistanceInput_iXC4eSyncPortB = 183, + Automation1PsoDistanceInput_iXC4eDrivePulseStream = 184, + Automation1PsoDistanceInput_iXC6ePrimaryFeedback = 185, + Automation1PsoDistanceInput_iXC6eAuxiliaryFeedback = 186, + Automation1PsoDistanceInput_iXC6eSyncPortA = 187, + Automation1PsoDistanceInput_iXC6eSyncPortB = 188, + Automation1PsoDistanceInput_iXC6eDrivePulseStream = 189, + Automation1PsoDistanceInput_iXL5ePrimaryFeedback = 190, + Automation1PsoDistanceInput_iXL5eAuxiliaryFeedback = 191, + Automation1PsoDistanceInput_iXL5eSyncPortA = 192, + Automation1PsoDistanceInput_iXL5eSyncPortB = 193, + Automation1PsoDistanceInput_iXL5eDrivePulseStream = 194, + Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis1 = 195, + Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis2 = 196, + Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis3 = 197, + Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis4 = 198, + Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis5 = 199, + Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis6 = 200, + Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis1 = 201, + Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis2 = 202, + Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis3 = 203, + Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis4 = 204, + Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis5 = 205, + Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis6 = 206, + Automation1PsoDistanceInput_iXR3SyncPortA = 207, + Automation1PsoDistanceInput_iXR3SyncPortB = 208, + Automation1PsoDistanceInput_iXR3DrivePulseStream = 209, + Automation1PsoDistanceInput_GI4DrivePulseStreamAxis1 = 210, + Automation1PsoDistanceInput_GI4DrivePulseStreamAxis2 = 211, + Automation1PsoDistanceInput_GI4DrivePulseStreamAxis3 = 212, + Automation1PsoDistanceInput_iXC2PrimaryFeedback = 213, + Automation1PsoDistanceInput_iXC2AuxiliaryFeedback = 214, + Automation1PsoDistanceInput_iXC2DrivePulseStream = 215, + Automation1PsoDistanceInput_iXC2ePrimaryFeedback = 216, + Automation1PsoDistanceInput_iXC2eAuxiliaryFeedback = 217, + Automation1PsoDistanceInput_iXC2eDrivePulseStream = 218, + Automation1PsoDistanceInput_iXL2ePrimaryFeedback = 219, + Automation1PsoDistanceInput_iXL2eAuxiliaryFeedback = 220, + Automation1PsoDistanceInput_iXL2eSyncPortA = 221, + Automation1PsoDistanceInput_iXL2eSyncPortB = 222, + Automation1PsoDistanceInput_iXL2eDrivePulseStream = 223, + Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis1 = 224, + Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis2 = 225, + Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis3 = 226, + Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis4 = 227, + Automation1PsoDistanceInput_iXI4AuxiliaryFeedback1 = 228, + Automation1PsoDistanceInput_iXI4AuxiliaryFeedback2 = 229, + Automation1PsoDistanceInput_iXI4AuxiliaryFeedback3 = 230, + Automation1PsoDistanceInput_iXI4AuxiliaryFeedback4 = 231, + Automation1PsoDistanceInput_iXI4SyncPortA = 232, + Automation1PsoDistanceInput_iXI4SyncPortB = 233, + Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis1 = 234, + Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis2 = 235, + Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis3 = 236, + Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis4 = 237, + +# @brief Specifies the PSO window input settings for each drive. +class Automation1PsoWindowInput: + Automation1PsoWindowInput_GL4PrimaryFeedbackAxis1 = 100, + Automation1PsoWindowInput_GL4PrimaryFeedbackAxis2 = 101, + Automation1PsoWindowInput_GL4IfovFeedbackAxis1 = 102, + Automation1PsoWindowInput_GL4IfovFeedbackAxis2 = 103, + Automation1PsoWindowInput_GL4AuxiliaryFeedbackAxis1 = 104, + Automation1PsoWindowInput_GL4AuxiliaryFeedbackAxis2 = 105, + Automation1PsoWindowInput_GL4SyncPortA = 106, + Automation1PsoWindowInput_GL4SyncPortB = 107, + Automation1PsoWindowInput_GL4DrivePulseStreamAxis1 = 108, + Automation1PsoWindowInput_GL4DrivePulseStreamAxis2 = 109, + Automation1PsoWindowInput_XL4sPrimaryFeedback = 110, + Automation1PsoWindowInput_XL4sAuxiliaryFeedback = 111, + Automation1PsoWindowInput_XL4sSyncPortA = 112, + Automation1PsoWindowInput_XL4sSyncPortB = 113, + Automation1PsoWindowInput_XL4sDrivePulseStream = 114, + Automation1PsoWindowInput_XR3PrimaryFeedbackAxis1 = 115, + Automation1PsoWindowInput_XR3PrimaryFeedbackAxis2 = 116, + Automation1PsoWindowInput_XR3PrimaryFeedbackAxis3 = 117, + Automation1PsoWindowInput_XR3PrimaryFeedbackAxis4 = 118, + Automation1PsoWindowInput_XR3PrimaryFeedbackAxis5 = 119, + Automation1PsoWindowInput_XR3PrimaryFeedbackAxis6 = 120, + Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis1 = 121, + Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis2 = 122, + Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis3 = 123, + Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis4 = 124, + Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis5 = 125, + Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis6 = 126, + Automation1PsoWindowInput_XR3SyncPortA = 127, + Automation1PsoWindowInput_XR3SyncPortB = 128, + Automation1PsoWindowInput_XR3DrivePulseStream = 129, + Automation1PsoWindowInput_XC4PrimaryFeedback = 130, + Automation1PsoWindowInput_XC4AuxiliaryFeedback = 131, + Automation1PsoWindowInput_XC4SyncPortA = 132, + Automation1PsoWindowInput_XC4SyncPortB = 133, + Automation1PsoWindowInput_XC4DrivePulseStream = 134, + XC4ePrimaryFeedback = 135, + XC4eAuxiliaryFeedback = 136, + XC4eSyncPortA = 137, + XC4eSyncPortB = 138, + XC4eDrivePulseStream = 139, + Automation1PsoWindowInput_XC6ePrimaryFeedback = 140, + Automation1PsoWindowInput_XC6eAuxiliaryFeedback = 141, + Automation1PsoWindowInput_XC6eSyncPortA = 142, + Automation1PsoWindowInput_XC6eSyncPortB = 143, + Automation1PsoWindowInput_XC6eDrivePulseStream = 144, + Automation1PsoWindowInput_XL5ePrimaryFeedback = 145, + Automation1PsoWindowInput_XL5eAuxiliaryFeedback = 146, + Automation1PsoWindowInput_XL5eSyncPortA = 147, + Automation1PsoWindowInput_XL5eSyncPortB = 148, + Automation1PsoWindowInput_XL5eDrivePulseStream = 149, + Automation1PsoWindowInput_XC2PrimaryFeedback = 150, + Automation1PsoWindowInput_XC2AuxiliaryFeedback = 151, + Automation1PsoWindowInput_XC2DrivePulseStream = 152, + Automation1PsoWindowInput_XC2ePrimaryFeedback = 153, + Automation1PsoWindowInput_XC2eAuxiliaryFeedback = 154, + Automation1PsoWindowInput_XC2eDrivePulseStream = 155, + Automation1PsoWindowInput_XL2ePrimaryFeedback = 156, + Automation1PsoWindowInput_XL2eAuxiliaryFeedback = 157, + Automation1PsoWindowInput_XL2eSyncPortA = 158, + Automation1PsoWindowInput_XL2eSyncPortB = 159, + Automation1PsoWindowInput_XL2eDrivePulseStream = 160, + Automation1PsoWindowInput_XI4PrimaryFeedbackAxis1 = 161, + Automation1PsoWindowInput_XI4PrimaryFeedbackAxis2 = 162, + Automation1PsoWindowInput_XI4PrimaryFeedbackAxis3 = 163, + Automation1PsoWindowInput_XI4PrimaryFeedbackAxis4 = 164, + Automation1PsoWindowInput_XI4AuxiliaryFeedback1 = 165, + Automation1PsoWindowInput_XI4AuxiliaryFeedback2 = 166, + Automation1PsoWindowInput_XI4AuxiliaryFeedback3 = 167, + Automation1PsoWindowInput_XI4AuxiliaryFeedback4 = 168, + Automation1PsoWindowInput_XI4SyncPortA = 169, + Automation1PsoWindowInput_XI4SyncPortB = 170, + Automation1PsoWindowInput_XI4DrivePulseStreamAxis1 = 171, + Automation1PsoWindowInput_XI4DrivePulseStreamAxis2 = 172, + Automation1PsoWindowInput_XI4DrivePulseStreamAxis3 = 173, + Automation1PsoWindowInput_XI4DrivePulseStreamAxis4 = 174, + Automation1PsoWindowInput_iXC4PrimaryFeedback = 175, + Automation1PsoWindowInput_iXC4AuxiliaryFeedback = 176, + Automation1PsoWindowInput_iXC4SyncPortA = 177, + Automation1PsoWindowInput_iXC4SyncPortB = 178, + Automation1PsoWindowInput_iXC4DrivePulseStream = 179, + Automation1PsoWindowInput_iXC4ePrimaryFeedback = 180, + Automation1PsoWindowInput_iXC4eAuxiliaryFeedback = 181, + Automation1PsoWindowInput_iXC4eSyncPortA = 182, + Automation1PsoWindowInput_iXC4eSyncPortB = 183, + Automation1PsoWindowInput_iXC4eDrivePulseStream = 184, + Automation1PsoWindowInput_iXC6ePrimaryFeedback = 185, + Automation1PsoWindowInput_iXC6eAuxiliaryFeedback = 186, + Automation1PsoWindowInput_iXC6eSyncPortA = 187, + Automation1PsoWindowInput_iXC6eSyncPortB = 188, + Automation1PsoWindowInput_iXC6eDrivePulseStream = 189, + Automation1PsoWindowInput_iXL5ePrimaryFeedback = 190, + Automation1PsoWindowInput_iXL5eAuxiliaryFeedback = 191, + Automation1PsoWindowInput_iXL5eSyncPortA = 192, + Automation1PsoWindowInput_iXL5eSyncPortB = 193, + Automation1PsoWindowInput_iXL5eDrivePulseStream = 194, + Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis1 = 195, + Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis2 = 196, + Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis3 = 197, + Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis4 = 198, + Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis5 = 199, + Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis6 = 200, + Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis1 = 201, + Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis2 = 202, + Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis3 = 203, + Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis4 = 204, + Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis5 = 205, + Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis6 = 206, + Automation1PsoWindowInput_iXR3SyncPortA = 207, + Automation1PsoWindowInput_iXR3SyncPortB = 208, + Automation1PsoWindowInput_iXR3DrivePulseStream = 209, + Automation1PsoWindowInput_GI4DrivePulseStreamAxis1 = 210, + Automation1PsoWindowInput_GI4DrivePulseStreamAxis2 = 211, + Automation1PsoWindowInput_GI4DrivePulseStreamAxis3 = 212, + Automation1PsoWindowInput_iXC2PrimaryFeedback = 213, + Automation1PsoWindowInput_iXC2AuxiliaryFeedback = 214, + Automation1PsoWindowInput_iXC2DrivePulseStream = 215, + Automation1PsoWindowInput_iXC2ePrimaryFeedback = 216, + Automation1PsoWindowInput_iXC2eAuxiliaryFeedback = 217, + Automation1PsoWindowInput_iXC2eDrivePulseStream = 218, + Automation1PsoWindowInput_iXL2ePrimaryFeedback = 219, + Automation1PsoWindowInput_iXL2eAuxiliaryFeedback = 220, + Automation1PsoWindowInput_iXL2eSyncPortA = 221, + Automation1PsoWindowInput_iXL2eSyncPortB = 222, + Automation1PsoWindowInput_iXL2eDrivePulseStream = 223, + Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis1 = 224, + Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis2 = 225, + Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis3 = 226, + Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis4 = 227, + Automation1PsoWindowInput_iXI4AuxiliaryFeedback1 = 228, + Automation1PsoWindowInput_iXI4AuxiliaryFeedback2 = 229, + Automation1PsoWindowInput_iXI4AuxiliaryFeedback3 = 230, + Automation1PsoWindowInput_iXI4AuxiliaryFeedback4 = 231, + Automation1PsoWindowInput_iXI4SyncPortA = 232, + Automation1PsoWindowInput_iXI4SyncPortB = 233, + Automation1PsoWindowInput_iXI4DrivePulseStreamAxis1 = 234, + Automation1PsoWindowInput_iXI4DrivePulseStreamAxis2 = 235, + Automation1PsoWindowInput_iXI4DrivePulseStreamAxis3 = 236, + Automation1PsoWindowInput_iXI4DrivePulseStreamAxis4 = 237, + +# @brief Specifies the PSO output pin settings for each drive. +class Automation1PsoOutputPin: + Automation1PsoOutputPin_GL4None = 100, + Automation1PsoOutputPin_GL4LaserOutput0 = 101, + Automation1PsoOutputPin_XL4sNone = 102, + Automation1PsoOutputPin_XL4sLaserOutput0 = 103, + Automation1PsoOutputPin_XR3None = 104, + Automation1PsoOutputPin_XR3PsoOutput1 = 105, + Automation1PsoOutputPin_XR3PsoOutput2 = 106, + Automation1PsoOutputPin_XR3PsoOutput3 = 107, + Automation1PsoOutputPin_XC4DedicatedOutput = 108, + Automation1PsoOutputPin_XC4AuxiliaryMarkerDifferential = 109, + Automation1PsoOutputPin_XC4AuxiliaryMarkerSingleEnded = 110, + XC4eDedicatedOutput = 111, + XC4eAuxiliaryMarkerDifferential = 112, + XC4eAuxiliaryMarkerSingleEnded = 113, + Automation1PsoOutputPin_XC6eDedicatedOutput = 114, + Automation1PsoOutputPin_XC6eAuxiliaryMarkerDifferential = 115, + Automation1PsoOutputPin_XC6eAuxiliaryMarkerSingleEnded = 116, + Automation1PsoOutputPin_XL5eDedicatedOutput = 117, + Automation1PsoOutputPin_XL5eAuxiliaryMarkerDifferential = 118, + Automation1PsoOutputPin_XL5eAuxiliaryMarkerSingleEnded = 119, + Automation1PsoOutputPin_XC2DedicatedOutput = 120, + Automation1PsoOutputPin_XC2eDedicatedOutput = 121, + Automation1PsoOutputPin_XL2eDedicatedOutput = 122, + Automation1PsoOutputPin_XI4DedicatedOutput = 123, + Automation1PsoOutputPin_iXC4DedicatedOutput = 124, + Automation1PsoOutputPin_iXC4AuxiliaryMarkerDifferential = 125, + Automation1PsoOutputPin_iXC4AuxiliaryMarkerSingleEnded = 126, + Automation1PsoOutputPin_iXC4eDedicatedOutput = 127, + Automation1PsoOutputPin_iXC4eAuxiliaryMarkerDifferential = 128, + Automation1PsoOutputPin_iXC4eAuxiliaryMarkerSingleEnded = 129, + Automation1PsoOutputPin_iXC6eDedicatedOutput = 130, + Automation1PsoOutputPin_iXC6eAuxiliaryMarkerDifferential = 131, + Automation1PsoOutputPin_iXC6eAuxiliaryMarkerSingleEnded = 132, + Automation1PsoOutputPin_iXL5eDedicatedOutput = 133, + Automation1PsoOutputPin_iXL5eAuxiliaryMarkerDifferential = 134, + Automation1PsoOutputPin_iXL5eAuxiliaryMarkerSingleEnded = 135, + Automation1PsoOutputPin_iXR3None = 136, + Automation1PsoOutputPin_iXR3PsoOutput1 = 137, + Automation1PsoOutputPin_iXR3PsoOutput2 = 138, + Automation1PsoOutputPin_iXR3PsoOutput3 = 139, + Automation1PsoOutputPin_GI4None = 140, + Automation1PsoOutputPin_GI4LaserOutput0 = 141, + Automation1PsoOutputPin_iXC2eDedicatedOutput = 143, + Automation1PsoOutputPin_iXL2eDedicatedOutput = 144, + Automation1PsoOutputPin_iXI4DedicatedOutput = 145, +""" + + +class DriveDataCaptureInput: + PositionCommand = 0 + PrimaryFeedback = 1 + AuxiliaryFeedback = 2 + AnalogInput0 = 3 + AnalogInput1 = 4 + AnalogInput2 = 5 + AnalogInput3 = 6 + + +class DriveDataCaptureTrigger: + PsoOutput = 0 + PsoEvent = 1 + HighSpeedInput0RisingEdge = 2 + HighSpeedInput0FallingEdge = 3 + HighSpeedInput1RisingEdge = 4 + HighSpeedInput1FallingEdge = 5 + AuxiliaryMarkerRisingEdge = 6 + AuxiliaryMarkerFallingEdge = 7 + + + + +class PsoOutputPin: + GL4None = 100, + GL4LaserOutput0 = 101, + XL4sNone = 102, + XL4sLaserOutput0 = 103, + XR3None = 104, + XR3PsoOutput1 = 105, + XR3PsoOutput2 = 106, + XR3PsoOutput3 = 107, + XC4DedicatedOutput = 108, + XC4AuxiliaryMarkerDifferential = 109, + XC4AuxiliaryMarkerSingleEnded = 110, + XC4eDedicatedOutput = 111, + XC4eAuxiliaryMarkerDifferential = 112, + XC4eAuxiliaryMarkerSingleEnded = 113, + XC6eDedicatedOutput = 114, + XC6eAuxiliaryMarkerDifferential = 115, + XC6eAuxiliaryMarkerSingleEnded = 116, + XL5eDedicatedOutput = 117, + XL5eAuxiliaryMarkerDifferential = 118, + XL5eAuxiliaryMarkerSingleEnded = 119, + XC2DedicatedOutput = 120, + XC2eDedicatedOutput = 121, + XL2eDedicatedOutput = 122, + XI4DedicatedOutput = 123, + iXC4DedicatedOutput = 124, + iXC4AuxiliaryMarkerDifferential = 125, + iXC4AuxiliaryMarkerSingleEnded = 126, + iXC4eDedicatedOutput = 127, + iXC4eAuxiliaryMarkerDifferential = 128, + iXC4eAuxiliaryMarkerSingleEnded = 129, + iXC6eDedicatedOutput = 130, + iXC6eAuxiliaryMarkerDifferential = 131, + iXC6eAuxiliaryMarkerSingleEnded = 132, + iXL5eDedicatedOutput = 133, + iXL5eAuxiliaryMarkerDifferential = 134, + iXL5eAuxiliaryMarkerSingleEnded = 135, + iXR3None = 136, + iXR3PsoOutput1 = 137, + iXR3PsoOutput2 = 138, + iXR3PsoOutput3 = 139, + GI4None = 140, + GI4LaserOutput0 = 141, + iXC2DedicatedOutput = 142, + iXC2eDedicatedOutput = 143, + iXL2eDedicatedOutput = 144, + iXI4DedicatedOutput = 145, + + + + + + + + + diff --git a/ophyd_devices/epics/devices/__init__.py b/ophyd_devices/epics/devices/__init__.py index 2b5c0c2..05a1546 100644 --- a/ophyd_devices/epics/devices/__init__.py +++ b/ophyd_devices/epics/devices/__init__.py @@ -29,5 +29,6 @@ from .eiger9m_csaxs import Eiger9McSAXS from .pilatus_csaxs import PilatuscSAXS from .falcon_csaxs import FalconcSAXS from .delay_generator_csaxs import DelayGeneratorcSAXS +from .AerotechAutomation1 import aa1Controller, aa1Tasks # from .psi_detector_base import PSIDetectorBase, CustomDetectorMixin